├── .gitignore ├── LICENSE.md ├── Logo.graffle ├── data.plist ├── image10.tiff └── image9.tiff ├── README.md ├── TbdArt.graffle ├── data.plist ├── image1.tiff ├── image2.tiff └── image4.png ├── book_cover.jpg ├── book_page_start.html ├── config.toml ├── content ├── 5-min-overview │ ├── 5trunk1.png │ ├── index.md │ └── trunk_pr.png ├── _index.md ├── alternative-branching-models │ ├── cascade1.png │ ├── cascade2.png │ ├── gitflow.png │ ├── githubflow1.png │ ├── githubflow3.png │ ├── index.md │ ├── mainline1.png │ ├── mainline2.png │ ├── mainline3.png │ └── mainline4.png ├── book │ └── index.md ├── branch-by-abstraction │ ├── cars.png │ └── index.md ├── branch-for-release │ ├── atscale.png │ ├── branch_for_release.png │ ├── branch_for_release2.png │ ├── branch_for_release3.png │ └── index.md ├── committing-straight-to-the-trunk │ └── index.md ├── concurrent-development-of-consecutive-releases │ └── index.md ├── context │ ├── index.md │ └── layer_cake.png ├── continuous-delivery │ ├── index.md │ └── pipelines2.png ├── continuous-integration │ ├── ci_types.png │ ├── ci_types2.png │ ├── index.md │ └── pipelines1.png ├── continuous-review │ └── index.md ├── contributions │ └── index.md ├── deciding-factors │ └── index.md ├── expanding-contracting-monorepos │ ├── car_segway.png │ └── index.md ├── feature-flags │ └── index.md ├── game-changers │ ├── away_from_tbd1.png │ ├── away_from_tbd2.png │ ├── away_from_tbd3.png │ ├── beneficial_to_both.png │ ├── google-tbd.png │ ├── index.md │ ├── mondrian.png │ ├── to_tbd1.png │ ├── to_tbd2.png │ ├── to_tbd3.png │ └── to_tbd3_but_secret.png ├── ix_key.png ├── key │ ├── index.md │ └── key.png ├── monorepos │ └── index.md ├── observed-habits │ └── index.md ├── publications │ ├── 2015_state_of_devops.png │ ├── 2016_state_of_devops.png │ ├── 2017_state_of_devops.png │ ├── build_quality_in.jpeg │ ├── cd-book.png │ ├── cdit_org_perf.png │ ├── ci.jpg │ ├── devopsHandbook.jpg │ ├── index.md │ ├── lean-enterprise.png │ ├── less_dogma.png │ ├── mcd.png │ └── scm-patterns.jpg ├── release-from-trunk │ ├── index.md │ ├── release_from_trunk.png │ ├── release_from_trunk2.png │ └── release_from_trunk3.png ├── short-lived-feature-branches │ ├── index.md │ ├── slfb_bad_sharing.png │ ├── slfb_pull-push.png │ └── slfb_working-copy.png ├── strangulation │ └── index.md ├── styles │ ├── index.md │ ├── styles-tradeoffs-c.png │ ├── styles-tradeoffs-l.png │ ├── styles-tradeoffs-r.png │ └── styles-tradeoffs.png ├── trunk1a.png ├── trunk1b.png ├── trunk1c.png ├── vcs-choices │ ├── index.md │ └── linus-git.png ├── vcs-features │ └── index.md └── youre-doing-it-wrong │ └── index.md ├── fix_html.py ├── footer_refs.py ├── static ├── .gitkeep └── google8536929dca466e24.html ├── themes └── hugo-material-docs │ ├── archetypes │ └── default.md │ ├── images │ ├── screenshot.png │ └── tn.png │ ├── layouts │ ├── 404.html │ ├── _default │ │ ├── __list.html │ │ └── single.html │ ├── index.html │ ├── partials │ │ ├── drawer.html │ │ ├── footer.html │ │ ├── footer_js.html │ │ ├── head.html │ │ ├── header.html │ │ ├── nav.html │ │ └── nav_link.html │ └── shortcodes │ │ ├── ext.html │ │ ├── note.html │ │ ├── quote.html │ │ └── warning.html │ ├── static │ ├── fonts │ │ ├── icon.eot │ │ ├── icon.svg │ │ ├── icon.ttf │ │ └── icon.woff │ ├── google8536929dca466e24.html │ ├── images │ │ ├── LogoSlim.png │ │ ├── ext.png │ │ ├── favicon.ico │ │ └── logo.png │ ├── javascripts │ │ ├── application.js │ │ └── modernizr.js │ └── stylesheets │ │ ├── application.css │ │ ├── highlight │ │ └── highlight.css │ │ ├── palettes.css │ │ └── temporary.css │ └── theme.toml ├── toc_number_applier.py └── toc_numberer.py /.gitignore: -------------------------------------------------------------------------------- 1 | public 2 | .idea 3 | npm-debug.log 4 | .DS_Store 5 | .netlify 6 | /book 7 | tempHugo/ 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The Hugo theme in use 'hugo-material-docs' has LICENSE detailed 2 | here - https://github.com/digitalcraftsman/hugo-material-docs/blob/master/LICENSE.md 3 | 4 | The content for this site is copyright of the contributors who 5 | by committing or contributing agree that it can be hosted on 6 | http://trunkbaseddevelopment.com 7 | 8 | They/we don't agree that it can be copied elsewhere, beyond short 9 | excerpts with clear links back to http://trunkbaseddevelopment.com -------------------------------------------------------------------------------- /Logo.graffle/data.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/Logo.graffle/data.plist -------------------------------------------------------------------------------- /Logo.graffle/image10.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/Logo.graffle/image10.tiff -------------------------------------------------------------------------------- /Logo.graffle/image9.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/Logo.graffle/image9.tiff -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Source for https://trunkbaseddevelopment.com 2 | 3 | # Running locally 4 | 5 | 6 | First: Install [hugo](https://gohugo.io/) for yourself 7 | 8 | Next: Clone this repo, and cd into the 'tbd' dir in question 9 | 10 | Next: Launch Hugo, like so: 11 | 12 | ``` 13 | hugo server 14 | ``` 15 | 16 | Next: Take a look on [localhost:1313](http://localhost:1313) 17 | 18 | Then edit away ... Hugo hot-reloads pages. 19 | 20 | 21 | # Contribution rules 22 | 23 | 1. Contribute if you have something to add to the site. 24 | 2. Keep the message multi-level (beginner, intermediate, advanced). 25 | 3. Understand that some people are diametrically opposed to this, and if you're to convince them at all, it will be very carefully. 26 | 4. No inflamed language, please. 27 | 5. Abide by [the golden rule](https://en.wikipedia.org/wiki/Golden_Rule) and generally write according to the 'High Esteem' value. 28 | 6. Links to Blog entries, tweets (etc) should go in a collapsed refs section without forward links inline. Stick to chronological order, per section. 29 | 7. Links to mainstream books, and Gartner-style reports, inline. Use affiliate codes that contribute to the original authors, if applicable. 30 | 8. Avoid H1 tags (single hash/pound '#') as the hugo theme doesn't do the right thing with them 31 | 9. Be careful with the dates in the front matter of the markdown sources. We're contriving them presently, as they guide article navigation 32 | 10. Don't shorten to TBD beyond the single use we already have. 33 | 11. Repo and Deps should be expanded to their long form 34 | 35 | # How to contribute? 36 | 37 | Pull-Requests, of course, if you're not a committer. 38 | 39 | English contributions (expansions of the material) to the `master` branch 40 | 41 | Chinese translations to the `cn` or [`tw`](https://tw.trunkbaseddevelopment.com/) branch. - should always reflect the English content, so we can use merge tracking keep the two in step. 42 | -------------------------------------------------------------------------------- /TbdArt.graffle/data.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/TbdArt.graffle/data.plist -------------------------------------------------------------------------------- /TbdArt.graffle/image1.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/TbdArt.graffle/image1.tiff -------------------------------------------------------------------------------- /TbdArt.graffle/image2.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/TbdArt.graffle/image2.tiff -------------------------------------------------------------------------------- /TbdArt.graffle/image4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/TbdArt.graffle/image4.png -------------------------------------------------------------------------------- /book_cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/book_cover.jpg -------------------------------------------------------------------------------- /book_page_start.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | baseurl = "https://trunkbaseddevelopment.com/" 2 | languageCode = "en-us" 3 | title = "Trunk Based Development" 4 | theme = "hugo-material-docs" 5 | metadataformat = "yaml" 6 | canonifyurls = true 7 | # Enable Google Analytics by entering your tracking id 8 | googleAnalytics = "UA-24415524-3" 9 | 10 | [params] 11 | # General information 12 | author = "paul-hammant" 13 | description = "A portal on this practice" 14 | copyright = "TrunkBasedDevelopment.com's contributors." 15 | 16 | # Repository 17 | provider = "GitHub" 18 | repo_url = "https://github.com/paul-hammant/tbd" 19 | 20 | version = "1.0.0" 21 | logo = "images/logo.png" 22 | favicon = "" 23 | 24 | permalink = "#" 25 | 26 | # Custom assets 27 | custom_css = [] 28 | custom_js = [] 29 | 30 | # Syntax highlighting theme 31 | highlight_css = "" 32 | 33 | [params.palette] 34 | primary = "blue" 35 | accent = "light-blue" 36 | 37 | [params.font] 38 | text = "Ubuntu" 39 | code = "Ubuntu Mono" 40 | 41 | [page] 42 | nextPrevInSectionSortOrder = 'desc' 43 | nextPrevSortOrder = 'desc' 44 | 45 | 46 | [social] 47 | twitter = "" 48 | github = "paul-hammant" 49 | email = "paul@hammant.org" 50 | 51 | [[menu.main]] 52 | name = "Introduction" 53 | url = "/" 54 | weight = 1 55 | 56 | [[menu.main]] 57 | name = "Context" 58 | url = "context/" 59 | weight = 5 60 | 61 | [[menu.main]] 62 | name = "Five-minute overview" 63 | url = "5-min-overview/" 64 | weight = 11 65 | 66 | [[menu.main]] 67 | name = "Deciding factors" 68 | url = "deciding-factors/" 69 | weight = 21 70 | 71 | [[menu.main]] 72 | name = "Version control system features" 73 | url = "vcs-features/" 74 | weight = 31 75 | 76 | [[menu.main]] 77 | name = "Version control system choices" 78 | url = "vcs-choices/" 79 | weight = 32 80 | 81 | [[menu.main]] 82 | name = "Feature flags" 83 | url = "feature-flags/" 84 | weight = 41 85 | 86 | [[menu.main]] 87 | name = "Branch by Abstraction" 88 | url = "branch-by-abstraction/" 89 | weight = 53 90 | 91 | [[menu.main]] 92 | name = "Branch for release" 93 | url = "branch-for-release/" 94 | weight = 55 95 | 96 | [[menu.main]] 97 | name = "Release from trunk" 98 | url = "release-from-trunk/" 99 | weight = 56 100 | 101 | [[menu.main]] 102 | name = "Styles and Trade-offs" 103 | url = "styles/" 104 | weight = 60 105 | 106 | [[menu.main]] 107 | name = "Continuous Integration (CI)" 108 | url = "continuous-integration/" 109 | weight = 61 110 | 111 | [[menu.main]] 112 | name = "Committing straight to the trunk" 113 | url = "committing-straight-to-the-trunk/" 114 | weight = 62 115 | 116 | [[menu.main]] 117 | name = "Short Lived Feature Branches" 118 | url = "short-lived-feature-branches/" 119 | weight = 63 120 | 121 | [[menu.main]] 122 | name = "Continuous Code Review" 123 | url = "continuous-review/" 124 | weight = 65 125 | 126 | [[menu.main]] 127 | name = "Continuous Delivery (CD)" 128 | url = "continuous-delivery/" 129 | weight = 71 130 | 131 | [[menu.main]] 132 | name = "Concurrent development of consecutive releases" 133 | url = "concurrent-development-of-consecutive-releases/" 134 | weight = 75 135 | 136 | [[menu.main]] 137 | name = "Application strangulation" 138 | url = "strangulation/" 139 | weight = 78 140 | 141 | [[menu.main]] 142 | name = "Observed habits" 143 | url = "observed-habits/" 144 | weight = 81 145 | 146 | [[menu.main]] 147 | name = "You're doing it wrong" 148 | url = "youre-doing-it-wrong/" 149 | weight = 91 150 | 151 | [[menu.main]] 152 | name = "Alternative branching models" 153 | url = "alternative-branching-models/" 154 | weight = 101 155 | 156 | [[menu.main]] 157 | name = "Monorepos" 158 | url = "monorepos/" 159 | weight = 111 160 | 161 | [[menu.main]] 162 | name = "Expanding Contracting Monorepos" 163 | url = "expanding-contracting-monorepos/" 164 | weight = 112 165 | 166 | [[menu.main]] 167 | name = "Game Changers" 168 | url = "game-changers/" 169 | weight = 121 170 | 171 | [[menu.main]] 172 | name = "Publications" 173 | url = "publications/" 174 | weight = 131 175 | 176 | [[menu.main]] 177 | name = "Book on this topic" 178 | url = "book/" 179 | weight = 132 180 | 181 | [[menu.main]] 182 | name = "Key to branch diagrams" 183 | url = "key/" 184 | weight = 141 185 | 186 | [[menu.main]] 187 | name = "Contributions" 188 | url = "contributions/" 189 | weight = 151 190 | 191 | [blackfriday] 192 | smartypants = true 193 | fractions = true 194 | smartDashes = true 195 | plainIDAnchors = true 196 | 197 | [sitemap] 198 | changefreq = "weekly" 199 | priority = 0.5 200 | filename = "sitemap.xml" 201 | -------------------------------------------------------------------------------- /content/5-min-overview/5trunk1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/5-min-overview/5trunk1.png -------------------------------------------------------------------------------- /content/5-min-overview/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-01-01T09:42:02+05:00 3 | title: Five-minute overview 4 | weight: 11 5 | --- 6 | 7 | ## Distance 8 | 9 | {{< quote title="Branches create distance between developers and we do not want that" >}} 10 | — Frank Compagner, Guerrilla Games 11 | {{< /quote >}} 12 | 13 | Assuming any network-accessible source control, physical distance is mitigated by AV technologies including 14 | screen sharing. So we will not worry about that so much these days. 15 | 16 | Frank's 'distance' is about the distance to the integration of code from multiple components/modules/sub-teams for a 17 | binary that could be deployed or shipped. The problematic distance is to code not yet in the single shared branch, 18 | that might: 19 | 20 | * break something unexpected once merged 21 | * be difficult to merge in. 22 | * not show that work was duplicated until it is merged 23 | * not show problems of incompatibility/undesirability that does not break the build 24 | 25 | Trunk-Based Development is a branching model that reduces this distance to the minimum. 26 | 27 | ## What it is 28 | 29 | {{< note title="Notes" >}} 30 | * Use of "Developers" throughout this site, means "QA-automators" for the same buildable thing, too. 31 | * When we say 'the trunk' on this site, it is just a branch in a single repository that developers in a team are focusing on 32 | for development. It may be called 'main'. That hints at the fact that the branch in question may literally not be 33 | called 'trunk' at all. 34 | {{< /note >}} 35 | 36 | There are many deciding factors before a development team settles on Trunk-Based Development, but here is a short overview 37 | of the practices if they do: 38 | 39 | ### Releasability of work in progress 40 | 41 | Trunk-Based Development will always be **release ready** 42 | 43 | If an executive manager visited the development team and commanded "Competitor X has launched feature Y, go 44 | live now with what we have", the worst response would be "give us one hour". The development team might have been very 45 | busy with tricky or even time-consuming tasks (therefore partially complete), but in an hour, they are able to go live 46 | with something just stabilized from the trunk. Perhaps they can do it in less than an hour. The rule, though, is to **never break 47 | the build**, and **always be release ready** because the CIO or the business may surprise you. 48 | 49 | #### Where releases happen 50 | 51 | A key facilitating rule is that Trunk-Based Development teams exclusively **either** release directly from the 52 | trunk - see [release from trunk](/release-from-trunk/), **or** they make a branch from the trunk specifically for 53 | the actual release. See [Branch for release](/branch-for-release/). 54 | Teams with a higher release cadence do the former, and those with a lower release cadence do the latter. 55 | 56 | ### Checking out / cloning 57 | 58 | All developers in a team working on an application/service, clone and checkout from the trunk. They will 59 | update/pull/sync from that branch many times a day, **knowing** that the build passes. Their fast 60 | source-control system means that their delays are a matter of a few seconds for this operation. They are now 61 | integrating their teammates' commits on an hour-by-hour basis. 62 | 63 | ### Committing 64 | 65 | Similarly, developers completing a piece of development work (changes to source code), that does not 66 | break the build, will commit it back to the trunk. That it does not break the build should be provable. The granularity of that commit (how many a developer 67 | would implicitly do a day) can vary and is learned through experience, but commits are typically small. 68 | 69 | The developer needs to run the build, to prove that they did not break anything with the commit **before** the commit 70 | is pushed anywhere. They might have to do an update/pull/sync before they commit/push the changes back to the team's 71 | version control server, and additional builds too. There's a risk of a race condition there, but let us assume that is not 72 | going to happen for most teams. 73 | 74 | ### Code Reviews 75 | 76 | The developer needs to get the commit reviewed. Some teams will count the fact that the code was 'pair programmed' 77 | as an automatic review. Other teams will follow a conventional design where the commit is marshaled 78 | for review before landing in the trunk. In modern portal solutions, marshaled nearly always means a branch/fork (Pull 79 | Request) that is visible to the team. 80 | 81 | ![](trunk_pr.png) 82 | ([key](/key/)) 83 | 84 | ^ the speech bubbles are stylized code review comments 85 | 86 | Code review branches can (and should) be 87 | deleted after the code review is complete and be very short-lived. This is tricky for teams new to Trunk Based 88 | Development. 89 | 90 | Note: You want to keep 91 | the commentary/approval/rejection that is part of the review for historical and auditing purposes, but you do not want to 92 | keep the branch. Specifically, you do not want the developers to focus on the branch after the code review and merge back 93 | to the trunk. 94 | 95 | ## A safety net 96 | 97 | [Continuous Integration](/continuous-integration/) (CI) daemons are set up to watch the trunk (and the 98 | [short-lived feature branches](/short-lived-feature-branches/) used in review), and as quickly and completely as possible 99 | loudly/visibly inform the team that the trunk is broken. Some teams will lock the trunk and roll-back changes. Others 100 | will allow the CI server to do that automatically. 101 | 102 | ![](5trunk1.png) 103 | ([key](/key/)) 104 | 105 | The high bar is verifying the commit before it lands in the trunk. Short-lived Pull Request branches are the modern 106 | place for that. 107 | 108 | ## Developer team commitments 109 | 110 | As stated, developers are pledging to be rigorous and not break the build. They're also going to need to consider 111 | the impact of their potentially larger commits, especially where renames or moves were wholesale, and adopt techniques 112 | to allow those changes to be more easily consumed by teammates. 113 | 114 | ## Drilling into 'Distance' 115 | 116 | Problematic 'distance' has a few tangible examples: 117 | 118 | * Late merges of development that happened more than a couple of days ago 119 | * Difficult merges in particular 120 | * A breaking build that lowers development team throughput, and diverts resources while it is being fixed 121 | 122 | # References elsewhere 123 | 124 | show references 125 | 126 |
127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
03 Sep 2009, MartinFowler.com article
FeatureBranch
135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 |
16 Jun 2015, Blog Entry
Organization Pattern: Trunk-Based Development
143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 |
04 Oct 2016, Blog Entry
Branches considered harmful
151 |
152 | 153 | 154 | -------------------------------------------------------------------------------- /content/5-min-overview/trunk_pr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/5-min-overview/trunk_pr.png -------------------------------------------------------------------------------- /content/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2015-01-01T21:07:33+01:00 3 | title: Introduction 4 | type: index 5 | weight: 1 6 | --- 7 | 8 | ## One line summary 9 | 10 | A source-control branching model, where developers collaborate on code in a single branch called 'trunk' *, 11 | resist any pressure to create other long-lived development branches by employing documented techniques. They 12 | therefore avoid merge hell, do not break the build, and live happily ever after. 13 | 14 | * *main* for the Git community since 2020 (`master` with unsavory connotations before) 15 | 16 | ## Shared branches off mainline/main/trunk are bad at any release cadence: 17 | 18 | ![](trunk1a.png) 19 | 20 | ## Trunk-Based Development For Smaller Teams: 21 | 22 | ![](trunk1b.png) 23 | 24 | ## Scaled Trunk-Based Development: 25 | 26 | ![](trunk1c.png) 27 | 28 | ## Elaboration, Claims and Caveats 29 | 30 | ![](ix_key.png) 31 | 32 | Trunk-Based Development is a key enabler of [Continuous Integration](/continuous-integration/) and by extension 33 | [Continuous Delivery](/continuous-delivery/). When individuals on a team are committing their changes to the trunk 34 | multiple times a day it becomes easy to satisfy the core requirement of Continuous Integration that all team 35 | members commit to trunk at least once every 24 hours. This ensures the codebase is always releasable on demand 36 | and helps to make Continuous Delivery a reality. 37 | 38 | The dividing line between small team Trunk-Based Development and scaled Trunk-Based Development is a subject to team size and commit rate consideration. The precise moment a dev team is no longer "small" and has transitioned to "scaled" is subject to practitioner debate. Regardless, teams perform a full "pre integrate" build (compile, unit tests, integration tests) on their dev workstations before committing/pushing for others (or bots) to see. 39 | 40 | ### Claims 41 | 42 | - You should do Trunk-Based Development instead of GitFlow and other branching models that feature multiple long-running branches 43 | - You can either do a direct to trunk commit/push (v small teams) or a Pull-Request workflow as long as those feature branches 44 | are short-lived and the product of a single dev-workstation , whether done solo, pair-programmed, or mob. If the mob is bigger and the change more all-encompassing, techniques should include feature flags, branch by abstraction and "don't break the build" 45 | 46 | Of course, "Dev workstation" requires some interpretation in recent years. Development may be in a VM, or a dev container of sorts. Those could be local or in a cloud somewhere. 47 | 48 | ### Caveats 49 | 50 | - Depending on the team size, and the rate of commits, 51 | [short-lived feature branches](/short-lived-feature-branches/) are used for 52 | code-review and build checking (CI), but not artifact creation or publication, to happen before commits land in the trunk for other developers to depend on. 53 | Such branches allow developers to engage in [eager and continuous code review](/continuous-review/) of contributions 54 | before their code is integrated into the trunk. Very small teams may [commit direct to the trunk](/committing-straight-to-the-trunk/). 55 | 56 | - Depending on the intended release cadence, there may be [release branches](/branch-for-release/) that are cut from the trunk on 57 | a just-in-time basis, are 'hardened' before a release (without that being a team activity), and **those branches are deleted** some time after release. Alternatively, there 58 | may also be no release branches if the team is [releasing from Trunk](/release-from-trunk/), and choosing a "fix 59 | forward" strategy for bug fixes. Releasing from trunk is also for high-throughput teams, too. 60 | 61 | - Teams should become adept with the related [branch by abstraction](/branch-by-abstraction/) technique for longer 62 | to achieve changes, and use [feature flags](/feature-flags/) in day to day development to allow for hedging on 63 | the order of releases (and other good things - see [concurrent development of consecutive releases](/concurrent-development-of-consecutive-releases/)) 64 | 65 | - If you have more than a couple of developers on the project, you are going to need to hook up a 66 | [build server](/continuous-integration/) to verify that their commits have **not broken the build** 67 | after they land in the trunk, and also when they are ready to be merged back into the trunk from a 68 | short-lived feature branch. 69 | 70 | - Development teams can casually flex up or down in size (in the trunk) without affecting throughput or quality. 71 | Proof? [Google does Trunk-Based Development](/game-changers/index.html#google-revealing-their-monorepo-trunk-2016) and 72 | have **35000 developers and QA automators** in that single [monorepo](/monorepos/) trunk, that in their case can 73 | [expand or contract](/expanding-contracting-monorepos/) to suit the developer in question. 74 | 75 | - People who practice the [GitHub-flow branching model](/alternative-branching-models/index.html#modern-claimed-high-throughput-branching-models) will feel 76 | that this is quite similar, but there is one small difference around where to release from. 77 | 78 | - People who practice the Gitflow branching model will find this **very different**, as will many developers used to 79 | the popular ClearCase, Subversion, Perforce, StarTeam, VCS [branching models of the past](/alternative-branching-models/index.html#legacy-branching-models). 80 | 81 | - [Many publications](/publications/) promote Trunk-Based Development as we describe it here. Those include the best-selling 'Continuous Delivery' and 'DevOps Handbook'. This should not even be controversial anymore! 82 | 83 | ## History 84 | 85 | Trunk-Based Development is not a new branching model. The word 'trunk' is referent to the concept of a growing tree, 86 | where the fattest and longest span is the trunk, not the branches that radiate from it and are of more limited length. 87 | 88 | It has been a lesser known branching model of choice since the mid-nineties and considered tactically since the eighties. 89 | The largest of development organizations, like Google (as mentioned) and Facebook practice it at scale. 90 | 91 | Over 30 years different [advances to source-control technologies and related tools/techniques](/game-changers/) have made 92 | Trunk-Based Development more (and occasionally less) prevalent, but it has been a branching model that many have stuck 93 | with through the years. 94 | 95 | ## This site 96 | 97 | This site attempts to collect all the related facts, rationale and techniques for Trunk-Based Development together 98 | in one place, complete with twenty-five diagrams to help explain things. All without using TBD as an acronym 99 | even ~~once~~ twice. 100 | -------------------------------------------------------------------------------- /content/alternative-branching-models/cascade1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/alternative-branching-models/cascade1.png -------------------------------------------------------------------------------- /content/alternative-branching-models/cascade2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/alternative-branching-models/cascade2.png -------------------------------------------------------------------------------- /content/alternative-branching-models/gitflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/alternative-branching-models/gitflow.png -------------------------------------------------------------------------------- /content/alternative-branching-models/githubflow1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/alternative-branching-models/githubflow1.png -------------------------------------------------------------------------------- /content/alternative-branching-models/githubflow3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/alternative-branching-models/githubflow3.png -------------------------------------------------------------------------------- /content/alternative-branching-models/mainline1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/alternative-branching-models/mainline1.png -------------------------------------------------------------------------------- /content/alternative-branching-models/mainline2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/alternative-branching-models/mainline2.png -------------------------------------------------------------------------------- /content/alternative-branching-models/mainline3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/alternative-branching-models/mainline3.png -------------------------------------------------------------------------------- /content/alternative-branching-models/mainline4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/alternative-branching-models/mainline4.png -------------------------------------------------------------------------------- /content/book/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2017-01-02T20:08:11+01:00 3 | title: Book 4 | weight: 132 5 | --- 6 | 7 | The TrunkBasedDevelopment.com site transformed into book, via scripts: 8 | 9 | * [English PDF](https://book.trunkbaseddevelopment.com/trunk_based_development_book.pdf) - interactive 10 | * [English MOBI](https://book.trunkbaseddevelopment.com/trunk_based_development_book.mobi) - for Amazon's Kindle 11 | * [English EPUB](https://book.trunkbaseddevelopment.com/trunk_based_development_book.epub) - for iBooks, Google Books, Nook, and others 12 | 13 | The book, like the site, will change going forward as the science and references grow. The book is free (as in beer) to 14 | download of course, but only if unchanged versus the version on the TrunkBasedDevelopment.com site. 15 | 16 | 17 | -------------------------------------------------------------------------------- /content/branch-by-abstraction/cars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/branch-by-abstraction/cars.png -------------------------------------------------------------------------------- /content/branch-by-abstraction/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-05-03T19:56:50+01:00 3 | title: Branch by Abstraction 4 | weight: 53 5 | --- 6 | 7 | Branch by Abstraction is a set-piece technique to effect a 'longer to complete' change in the trunk. Say a developer 8 | (or a pair of developers), has a change that is going to take five days to complete. There could easily be pressure to 9 | create a branch for this - somewhere that can be unstable for a period of time before it completes (and gets merged 10 | back somewhere). 11 | 12 | There may be some aspect of repetition to the coding activities that makes it longer to complete. No matter, the 13 | change was predicted as being time-consuming, complex, destabilizing/disruptive to everyone else in the development team. 14 | 15 | **Rules:** 16 | 17 | 1. There are also a lot of developers already depending on 18 | the code that is subject of the 'longer to complete' change, and we do not want them to be slowed down in any way. 19 | 2. No commit pushed to the shared repository should jeopardize the ability to go live. 20 | 21 | ## Ideal steps 22 | 23 | For simplicity's sake, let us say there is code that is 'to be replaced', code 'to be introduced'. 24 | 25 | 1. Introduce an abstraction around the code that is to be replaced, and commit that for all to see. If needed, this 26 | can take multiple commits. None of those are allowed to break the build, and all of them could be pushed to the shared 27 | repository in order, and as done. 28 | 2. Write a second implementation of the abstraction for the to-be-introduced code, and commit that, but maybe as 29 | 'turned off' within the trunk so that other developers are not depending on it yet. If needed, this can take multiple 30 | commits as above. The abstraction from #1 may also be occasionally tweaked, but must follow the same rule - do not 31 | break the build. 32 | 3. Flip the software 'off' switch to 'on' for the rest of the team, and commit/push that. 33 | 4. Remove the to-be-replaced implementation 34 | 5. Remove the abstraction 35 | 36 | Hopefully, your team uses an IDE that can perform complex refactorings on sets on checkouts, in a way that running the build 37 | after each is an uneventful validation of the refactorings. 38 | 39 | ## Contrived example 40 | 41 | Let's talk about a car having its wheels upgraded. We should never forget that software engineering is 42 | nothing like conventional construction, and we want to ram that home. At least, it is nothing like conventional 43 | construction where we are not talking about a production line. 44 | 45 | ### Rules 46 | 47 | 1. Mechanics must be able to simultaneously work on the upholstery, engine, etc. 48 | 2. The car must be drivable after every change. 49 | 50 | ### Steps 51 | 52 | All steps are efficiently performed raised up on car jacks/lifts/ramps, before lowering the car to the ground again. 53 | 54 | 1. One wheel is removed, put in a container that looks pretty much like a wheel (rotates around an axis, 55 | can bear weight) and replaced on the car. If driven this wheel functions exactly like the 56 | other three wheels. 57 | 2. The wheel-like container gains a second better/desired/alternate wheel, within exactly the same 58 | physical space (magically). A switch is added inside the car to allow the choice of wheel to be switched conveniently 59 | - perhaps only before the engine is started, though. 60 | 3. The same two operations (#1 and #2) are performed on the other three wheels. Or maybe #1 is done four times, 61 | followed by #2 four times. The Mechanics experience will guide which is most efficient. 62 | 4. After determining that the new wheels are better, the old wheels are removed from the wheel-like containers 63 | and are sent for recycling. 64 | 5. The wheel-like containers are also removed from the new wheels, either one by one or all four simultaneously. 65 | 66 | At any stage, when lowered from the jacks/lift/ramps, the car could have been driven (a 'ready to go-live' metaphor). 67 | 68 | ![](cars.png) 69 | 70 | We said 'jacks' above, because that's what mechanics use in real life. Software, however, does not follow the rules 71 | of gravity, or many of the costs of actual construction. With an IDE for a glove, a single finger could reposition 72 | the car in 3D space to allow easy replacement of the wheels. 73 | 74 | ## Software example 75 | 76 | A documented case is ThoughtWorks' Go CI-daemon. They changed an Object-Relational mapping library (for persistence), 77 | while not slowing down teammates development activities (rule 1), and not jeopardizing the ability to go live (rule 2). 78 | 79 | Going from "iBatis" to "Hibernate" for a bunch of reasons, was their plan. 80 | 81 | They: 82 | 83 | 1. Introduced an abstraction around the classes/components using iBatis directly, and ensured that all 84 | classes/components indirectly referring to iBatis were changed to refer to the abstraction instead. 85 | 2. Wrote a second implementation of the abstraction, introducing Hibernate to the codebase, perhaps tweaking the 86 | abstraction where needed. 87 | 3. Did a tiny commit that turned on Hibernate for all teammates. 88 | 4. Removed iBatis, then the abstraction and the on/off old/new switch. 89 | 90 | As it happens you could leave the abstraction in place, if your unit tests are able to benefit because of the 91 | possibility of another seam that can be mocked. 92 | 93 | ## Secondary benefits 94 | 95 | ### Cheaply pause and resume 'migrations' 96 | 97 | The migration from old to new can be paused and resumed later casually. This is because the build guards the 98 | second, incomplete, implementation. It does so merely because of a compile stage that turns the abstraction and somewhere 99 | between 1 to 2 implementation into object code. If there are unit tests for the two alternates, then even more so. 100 | 101 | If on a real branch, the casual restart of the paused initiative is missing. There's possibly an exponential cost of 102 | restart given the elapsed time since the initiative was paused. 103 | 104 | Pause and resume is much more likely in an enterprise development organization that does not have limitless coffers. 105 | 106 | ### Cancellation of a project is still cheap 107 | 108 | In the case of abandonment, deleting a real long running feature branch is cheaper, but deletion of a 109 | branch by abstraction *thing* is only incrementally more expensive. 110 | 111 | ## Not a panacea 112 | 113 | Branch by Abstraction does not suit all 'change' situations. 114 | 115 | One is when you have got to support old APIs and previous releases for more than a short period of time. I.e. when your 116 | dependent customers (or detached clients apps) can choose their own upgrade moment. 117 | 118 | Some years ago the KDE team was mulling their release 5.0 strategy, and wanting to remain parallel to changes in 4.0, so as not to make 119 | mistakes that they had done 120 | previously{{< ext url="http://tech.slashdot.org/story/11/08/07/2128222/KDE-Frameworks-50-In-Development" >}}. TODO: circle back. 121 | 122 | ## Dedicated website for this procedure 123 | 124 | In early 2018, a Branch by abstraction was created to further drill into this concept{{< ext url="https://www.branchbyabstraction.com" >}}. 125 | 126 | ## History 127 | 128 | Teams employed Branch by Abstraction many years before it got its name (Stacy Curl named it in 2007), but it is 129 | unknown when the first implementation was. Before the adoption of BbA, teams **had to** make a branch for the 130 | big lengthy disruptive change, or do it with an incredible amount of choreography: "hey everyone, take a week of 131 | vacation now". 132 | 133 | With the Branch by Abstraction technique, Trunk-Based Development was less likely to be temporarily or permanently 134 | abandoned for a multi-branch model. 135 | 136 | # References elsewhere 137 | 138 | show references 139 | 140 |
141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 |
26 Apr 2007, Blog Entry
Introducing Branch by Abstraction
149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 |
05 May 2011, ContinuousDelivery.com article
Make Large Scale Changes Incrementally with Branch By Abstraction
157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 |
21 Jun 2013, Blog Entry
Branching Strategies: Feature Branches vs Branch by Abstraction
165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 |
14 Oct 2013, Blog entry
Application Pattern: Verify Branch By Abstraction
173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 |
07 Jan 2014, MartinFowler.com article
BranchByAbstraction
181 |
182 | -------------------------------------------------------------------------------- /content/branch-for-release/atscale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/branch-for-release/atscale.png -------------------------------------------------------------------------------- /content/branch-for-release/branch_for_release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/branch-for-release/branch_for_release.png -------------------------------------------------------------------------------- /content/branch-for-release/branch_for_release2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/branch-for-release/branch_for_release2.png -------------------------------------------------------------------------------- /content/branch-for-release/branch_for_release3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/branch-for-release/branch_for_release3.png -------------------------------------------------------------------------------- /content/branch-for-release/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-05-05T19:56:50+01:00 3 | title: Branch for release 4 | weight: 55 5 | --- 6 | 7 | {{< quote title="Branch: only when necessary, on incompatible policy, late, and instead of freeze" >}} 8 | — Laura Wingerd & Christopher Seiwald
9 | (1998's High-Level SCM Best Practices white paper from Perforce) 10 | {{< /quote >}} 11 | 12 | If a team is pushing production releases monthly, then they are also going to have to push bug-fix releases 13 | between planned releases. To facilitate that, it is common for Trunk-Based Development Teams to make a release 14 | branch on a just in time basis - say a few days before the release. That becomes a stable place, given the developers 15 | are still streaming their commits into the trunk at full speed. 16 | 17 | The incompatible policy (ref Wingerd & Seiwald above), that the release branch "should not receive continued development work". 18 | 19 | ![](branch_for_release.png) 20 | ([key](/key/)) 21 | 22 | ^ Trunk, two and a half release branches, five releases (two planned, three unplanned), and two cherry-pick bug fixes 23 | 24 | {{< note title="CD teams do not do release branches" >}} 25 | High throughput, [Continuous Delivery](/continuous-delivery/) teams can ignore this - if they had a lemon in production, they choose a 26 | roll-forward strategy for solving it, meaning the fix for a bug is in the trunk, and the release to production is from the trunk. 27 | {{< /note >}} 28 | 29 | ## Who is committing where? 30 | 31 | Developers are committing (green dots) at the highest throughput rate to the trunk, and do not slow up or freeze around a 32 | branch-cut or with proximity to a release. Developers as a group are **not** committing to the release branch (see below). 33 | 34 | ![](branch_for_release2.png) 35 | ([key](/key/)) 36 | 37 | The branch cut itself is a commit. Subversion and Perforce would technically have a bigger commit here, but all 38 | VCS systems in use today would count the commit as 'lightweight' in terms of its impact on the history/storage, 39 | and the time taken to create. 40 | 41 | That red dot is an accidental build break that was fixed (somehow) soon after. 42 | 43 | ## Late creation of release branches 44 | 45 | Some teams [release from a tag on the trunk](/release-from-trunk/) and do not create a branch at that time. That in 46 | itself is **an alternate practice to this one, "branch for release"**. 47 | 48 | Those teams wait for a bug that needs fixing for a released, before creating a branch from the release tag (if they are 49 | not going to just issue another release from the trunk). 50 | 51 | Brad Appleton points out that many do not realize that branches can be created **retroactively**. That is taken advantage 52 | of here in the case of bugs after "release from a tag", or even changes for point releases. 53 | 54 | ## Fix production bugs on Trunk 55 | 56 | The best practice for Trunk-Based Development teams is to reproduce the bug on the trunk, fix it there with a test, 57 | watch that be verified by the CI server, then cherry-pick that to the release branch and wait for a CI server 58 | focusing on the release branch to verify it there too. Yes, the CI pipeline that guards the trunk is going to 59 | be duplicated to guard active release branches too. 60 | 61 | {{< warning title="A cherry-pick is not a regular merge" >}} 62 | A cherry-pick merge takes a specific commit (or commits) and merges that to the destination branch. It skips 63 | one or more commits that happened before it, but after the branch was cut. All VCS tools track which commits 64 | have been merged and which ones not, so you can do more cherry picks later. 65 | {{< /warning >}} 66 | 67 | ### Cherry-picks from the trunk to branch ONLY 68 | 69 | You should not fix bugs on the release branch in the expectation of cherry-picking them back to the trunk. 70 | Why? Well in case you forget to do that in the heat of the moment. Forgetting means a regression in production some 71 | weeks later (and someone getting fired). It can happen if things are being fixed in the night by a tired developer who 72 | wants to get back to bed. 73 | 74 | ![](branch_for_release3.png) 75 | ([key](/key/)) 76 | 77 | This rule for Trunk Based Development remains difficult to accept, even within teams practicing everything else about 78 | Trunk-Based Development. It takes just one regression though for a policy change to be made for the team. 79 | 80 | Of course, sometimes you **absolutely cannot** reproduce the bug on trunk. In that case you have to do it the other way round, despite 81 | everything mentioned above, but understand you have introduced risk of regression. 82 | 83 | ### Google's Rachel Potvin on Cherry picks 84 | 85 | In a talk at the @Scale conference in Sept 2015, "Why Google Stores Billions of Lines of Code in a Single Repository", 86 | there was a slide that depicts cherry-picks in a branch diagram: 87 | 88 | ![](atscale.png) 89 | 90 | The presenter, Rachel Potvin, said (14 mins in): 91 | 92 | "So at Google we do what's called Trunk-Based Development. I should note that it is the combination of Trunk-Based Development with a centralized repository that really defines the monolithic model of source code management. So 93 | what Trunk-Based Development means for us that typically Piper users all work from HEAD or a single copy of the most recent version of the codebase. When developers commit to Piper their changes are immediately visible and usable by other engineers. Branching for development at Google is exceedingly rare and Trunk-Based Development is beneficial partly because you avoid the painful merges that often occur when you need to reconcile long lived branches. Branches however are used for releases. **So a release branch is typically a snapshot from trunk with an 94 | optional number of cherry picks that are developed on trunk and then pulled into the branch**." 95 | 96 | We've bolded the cherry-pick bit ourselves. Readers with beady eyes will note that Rachel alludes to 97 | dev branches other than trunk for 'rare' reasons. We may cheekily suggest that Google should learn a little more about [Branch by Abstraction](/branch-by-abstraction/). 98 | 99 | ### Merge Meister role 100 | 101 | The process of merging commits from trunk to the release branch using 'cherry pick' is a role for a single developer 102 | in a team. Or dev pair, if you are doing Extreme Programming. Even then, it is a part time activity. The dev or pair 103 | probably needs to police a list of rules before doing the cherry pick. Rules like which business representative 104 | signed off on the merge. Perhaps the role should also rotate each day. 105 | 106 | Some teams update a wiki to audit what made it to the release branch after branch cut, and some use ticket system as 107 | this by its nature interrupting and requiring of an audit trail of approvals. 108 | 109 | ## Patch releases 110 | 111 | It could be that your team has pushed a release out from a release branch, and now has a bug to remediate in 112 | production. If the release cadence suits it, a cherry-pick of a bug fix from the trunk to the release branch 113 | and a point release from the same branch is fine. 114 | 115 | ### Tag instead of branch 116 | 117 | Releasing from a tag on the trunk is a decent optimization for many teams, if possible. The tag could be numbered for 118 | the release (say v1.1.1), and the branch can be avoided completely. Perhaps if there is a bug in production and a branch 119 | is retroactively created from that tag, and the patch release (see above) can happen from there. 120 | 121 | ## Release branch deletion 122 | 123 | Release branches are deleted some time after release activity from them ceases. Not immediately, but when it is clear release is no longer in production. Release branches are NOT merged back into trunk. 124 | That is usually when releases from succeeding release branches have gone live. This is a 125 | harmless tidying activity - branches can be undeleted again easily enough in all VCS choices. In git, a tag needs to be created from the released commit before deleting the release branch, since dangling commits will be garbage collected. 126 | 127 | # References elsewhere 128 | 129 | show references 130 | 131 |
132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 |
1998, White Paper
High-level Best Practices in Software Configuration Management
140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 |
2018, Edward Thompson on MSDN
Release Flow: How We Do Branching on the VSTS Team
148 |
149 | -------------------------------------------------------------------------------- /content/committing-straight-to-the-trunk/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-06-02T20:10:46+01:00 3 | title: Committing straight to the trunk 4 | weight: 62 5 | --- 6 | 7 | Some teams will choose to commit/push straight to the trunk. Most likely it is because they are a small team with each 8 | team member knowing what the others are up to. **Their build is probably fast and relatively exhaustive**, and they 9 | may well seldom experience a build breakage. If the build does break (post integration into trunk/main) then they most likely 'revert' the 10 | commit straight away, possibly locking the trunk for a short period of time while that is performed. If the team is 11 | really small (say three or four), in which case the team might allow someone to fix the build quickly and commit that 12 | in order to get the build green again. 13 | 14 | In the 2000's many Trunk-Based Development teams might have numbered up to 100 committers. They may have been extremely 15 | rapid with their `reverts` (lock trunk, revert, kick off the CI daemon again, unlock trunk if green again). If there was no build-automation server, they would 16 | have performed check-in activities that 1997's C3 team{{< ext url="https://en.wikipedia.org/wiki/Chrysler_Comprehensive_Compensation_System" >}} 17 | would have recognized, because they wanted that human 18 | assurance that gated check-ins are all that is needed to keep the build green. Namely, developers holding an 19 | "I/we are checking in now, nobody else should be". They run the full build after bringing their checkout up to date 20 | and commit/push if green. Indeed that ceremony is a key part of the [Continuous Integration](/continuous-integration/) advances 21 | and is now part of Agile generally, and Extreme Programming in particular. These days teams doing this practice are likely 22 | to be much smaller (say less than 16) because of the advent of alternatives (see below). That said, there are still some large teams 23 | working this way. 24 | 25 | # Benefits 26 | 27 | It is easier to objectively verify the correctness of your commits yourself (optimally with a pair-programming partner), then commit/push at moments of convenience. That is, easier than pushing into a code-review system for approval from a teammates that risks being interruptive to their workflow. Indeed there is a greater likelihood that this this style or working becomes a flow of small commits into the trunk, with each of those being an incremental step forward, and perfectly able to go-live itself while the larger story/card remains incomplete. 28 | 29 | # Challenges 30 | 31 | Committing (and pushing) straight to the trunk has a challenge. Principally, someone could commit/push code that breaks the build, and the server(s) setup to guard Continuous Integration don't catch that for some time after the commit is available for teammates to pull/sync to their dev-workstation for unrelated work. 32 | 33 | Risk mitigation is **everyone** running the full build (the same build the CI demon would do) before the commit/push, and only pushing to 34 | trunk if that passes. **This is an essential integration activity**. This is the habit of the XP teams from the end of the 90's, and there's 35 | no reason any team would dispense with that in the years since. Indeed, it is valuable for ALL branching models. 36 | 37 | If this is locked in as a team requirement, your new challenge is to keep the full build fast. Fast is say one minute, and slow is ten or above. Compile and pure unit tests (no threads, sockets, file IO) is where good builds focus their development effort. Any following "integration test" build steps that use threads, listen on sockets, or do significant file IO should be minimized as far as possible without reducing meaningful coverage. The best trick for that is changing some integration tests into pure unit tests, which isn't always easy. 38 | 39 | Some teams have revert policies for commits that land in trunk (or main) that are proven as "broken" later in CI. That could be an activity for a build-cop who's going to communicate with the dev team about locking the trunk to achieve that. Or it could be a bot activity and happen instantly, as Google do in-house (35K committers in one trunk). 40 | 41 | Some teams have some scripting in place to ensure that developers only pull/sync commits to their dev-workstations that CI has marked as passing. That could be as simple as keeping a commit ID (number or hash depending on your VCS tool) on a website somewhere, and writing a wrapper script for git-pull (or svn up) that ignores commits that happened after that one. Pushing back, with that way of working is harder in Git and Mercurial as they requires you to have pulled HEAD revision before you can push back. Subversion and Perforce don't have that limitation. 42 | 43 | # Alternatives to committing straight to the trunk 44 | 45 | That modern alternative that allows development teams to scale up without having a bottleneck around check-ins or increased risk of broken builds: [Short-Lived Feature Branches](/short-lived-feature-branches/). 46 | 47 | There are also teams that send patches to review systems like Gerrit and Rietveld, instead of committing/pushing straight to trunk/main. Google pioneered this with their in-house Mondrian system in 2006, and Gerrit and Rietveld were made in the image of that. Facebook's Phabricator is another that came later. As well as code review, build-automation systems objectively verify the correctness of the proposed changes, leaving you with high confidence that the following merge/integration into trunk/main will yield a similar positive result when the same infrastructure kicks in later for CI purposes. It is important to note that the automation you attach to commits/pushes to non-trunk branches (or patch queue/review systems) is not Continuous Integration itself. 48 | 49 | These two alternatives, as well as committing straight to the trunk are compared in [Styles and Trade-offs](/styles/). 50 | -------------------------------------------------------------------------------- /content/concurrent-development-of-consecutive-releases/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-07-05T20:10:46+01:00 3 | title: Concurrent development of consecutive releases 4 | weight: 75 5 | --- 6 | 7 | ## Concurrent Development? 8 | 9 | Your company wants a stream of major functionality to arrive in the application you are pushing live at a regular 10 | cadence. Because you are good Extreme Programmers, you know that consecutive development of consecutive releases 11 | is best. However, the effort and length of time needed to complete each major piece of functionality is 12 | large enough to require different project teams cooperating towards that plan. Some of those teams will be within 13 | the same codebase. Some may be dependent services that the application will invoke over the wire. Not everything 14 | is equal effort it seems, yet the business wants a specific rollout, including dates and can plan that even eighteen 15 | months ahead. They are very specific because there is an impact on the user community (staff, clients, customers or 16 | members of the public). Driving departments may include training, marketing, finance. 17 | 18 | ## Oops? 19 | 20 | What you have got is the perfect setup for disaster born from the random bad news events. Things that can and do 21 | happen in software development. 22 | 23 | Or perhaps one thing was underestimated by 50% and that is realized later rather than sooner. 24 | Should all of the following releases slip too, assuming the company did not attempt to throw bodies at it in an attempt 25 | to solve it? We all know of Fred Brook's Mythical Man-Month{{< ext url="https://books.google.com/books/about/The_Mythical_Man_Month_Anniversary_Editi.html?id=Yq35BY5Fk3gC" >}} 26 | and Edward Yourdon's Death March{{< ext url="https://books.google.com/books/about/Death_March.html?id=FdAZUX9H_gAC" >}}. 27 | 28 | ## Reorder Releases? 29 | 30 | One compelling answer is to change the order of releases. To the business, that could be a relief even if it requires 31 | re-planning and problems around marketing/education given the impacted staff, clients, customers or members of the 32 | public. 33 | 34 | ## Un-merge? 35 | 36 | The trouble is that the development teams might have to face a selective un-merge or commenting-out frenzy to support that, depending on 37 | what had merged already. Different branching models have different merge impacts and are either early or late in terms 38 | of keenness for the act of merging. That in itself is disruptive to the business, as they fear and probably witness 39 | additional delays because of the retooling and new-found nerves. 40 | 41 | ## Flags, abstractions, and pipelines 42 | 43 | If your team has institutionalized Trunk-Based Development, [Feature Flags](/feature-flags/) pluggable components based 44 | on abstractions (not a world apart from [Branch by Abstraction](/branch-by-abstraction/)), it is in a perfect position 45 | to reorder releases, and only have a small impact on the throughput of the development team. 46 | 47 | ### Case study 48 | 49 | In a real-life case study for an airline in 2012, late in development for the planned release a partner said that they could 50 | not, in fact, meet that date. Their side of the integration was not going to be ready. The airline was code complete but now had to 51 | reorder releases. The development team's management feared some downtime while the mess was sorted out. The team in question 52 | was able to spin up a new CI pipeline, with the flags/toggles flipped to show the new permutation of features. 53 | The new CI pipeline confirmed what they had already seen on the command-line build, that there were failures in the 54 | automated tests (and something in a page did not quite look right anyway). A couple of quick fixes later, and the development 55 | team assured the airline's management that the releases could reasonably happen in any order. 56 | 57 | Choosing Trunk-Based Development, [Feature Flags](/feature-flags/) and [Branch by Abstraction](/branch-by-abstraction/) 58 | could be said to be a **hedging strategy** against the costs of larger scheduling changes. 59 | 60 | {{< warning title="Consecutive development of consecutive releases is by far superior!" >}} 61 | Every high throughput Extreme Programming team will tell you that finishing and shipping a release before starting work 62 | as a team on the next releasable slice of work is much better than attempting to do concurrent development of 63 | consecutive releases. Sure, some teammates (PM, BA, tech leads) are looking a couple of weeks ahead to make sure that 64 | everything is ready for development and QA automation on a just in time basis but the majority of the dev team will 65 | only pick up new release work as the previous one has been pushed into production. 66 | {{< /warning >}} 67 | 68 | 69 | # References elsewhere 70 | 71 | show references 72 | 73 |
74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 |
19 Mar 2013, Blog Entry
The Cost of Unmerge
82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 |
14 Jul 2013, Blog Entry
Legacy Application Strangulation: Case Studies
90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 |
10 Oct 2014, Conference Talk
Trunk-Based Development in the Enterprise - Its Relevance and Economics
98 |
99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /content/context/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2015-01-05T09:42:02+05:00 3 | title: Context 4 | weight: 5 5 | --- 6 | 7 | A development team wanting to go ahead with Trunk-Based Development have prerequisite achievements. Once the team has 8 | done a few commits in the correct style, other things are now facilitated. We can show this as a layer cake: 9 | 10 | ![](layer_cake.png) 11 | 12 | DevOps is encompassing too. At the very least, the expansion of development best practices into operations heartlands. 13 | 14 | ## Trunk-Based Development prerequisites 15 | 16 | (layers below it) 17 | 18 | ### Solid Development Infrastructure 19 | 20 | Installation of your VCS technology is part of a foundational development infrastructure that includes developer 21 | workstations or laptops that are appropriate for build testing and running of the application or service being made. 22 | Developers running the application only need it to be functionally viable. It doesn't have to match the performance 23 | of the expected production environment, and it is OK for it to be non-functionally different in other ways too. 24 | 25 | In the modern DevOps era, this probably means Infrastructure as Code. 26 | 27 | ## Trunk-Based Development facilitates 28 | 29 | (layers above it) 30 | 31 | ### Continuous Integration 32 | 33 | [Continuous Integration](/continuous-integration/) (CI) has been in practice since the 34 | mid-nineties in its modern incarnation (integrating to a shared code line frequently and 35 | testing that). 36 | 37 | Importantly, the reader should understand that there is a large overlap between Trunk-Based Development and 38 | Continuous Integration, as defined by its definers and documenters. Whereas Trunk-Based Development focuses on a 39 | pure source-control workflow and an individual contributor's obligations to that, Continuous Integration focuses 40 | equally on that and the need to have machines issue early warnings on breakages and incompatibilities. 41 | 42 | ### Continuous Delivery 43 | 44 | [Continuous Delivery](/continuous-delivery/) (CD) is a layer on top of that, has been practiced since the mid-2000's, and 45 | documented in Jez Humble and Dave Farley's book of the same name in 2010. This site gives a 5% summary of the 46 | practice. The reader should dive into the Book and associated site, without delay. 47 | 48 | ### Lean Experiments 49 | 50 | With CD locked in, continual improvement experiments can happen with a focus on time through "the machine" that is your 51 | development and delivery operation. The experiments should draw off the field of science that is "Lean" so that the 52 | impact of each experiment can be measured against predictions and decisions made appropriately following it. 53 | 54 | Lean Experiments can happen in any development team on any project but work **best** on foundations that 55 | are solid. Specifically, the solid foundations of Trunk-Based Development, CI, and CD. 56 | 57 | This site does not touch on Lean Experiments beyond this section, but the reader should strive to understand that field 58 | of science when the lower layers of the stylized cake are solid. 59 | -------------------------------------------------------------------------------- /content/context/layer_cake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/context/layer_cake.png -------------------------------------------------------------------------------- /content/continuous-delivery/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-07-01T20:10:46+01:00 3 | title: Continuous Delivery (CD) 4 | weight: 71 5 | --- 6 | 7 | Continuous Delivery is the practice expanding your Continuous Integration (CI) usage to automatically 8 | re-deploy a proven build to a QA or UAT environment. If the bounce time for a deployment is quick enough, 9 | then it could be that you are doing that for every commit that lands in the shared trunk. The Radiator's pipeline view would 10 | become: 11 | 12 | ![](pipelines2.png) 13 | 14 | The [bestselling book of the same name](/publications/index.html#continuous-delivery-july-27-2010) by Jez Humble and Dave Farley, 15 | details the 'marching orders' for many companies, where there is whole dev-team improvement agenda. 16 | 17 | {{< warning title="A Layer above Trunk-Based Development" >}} 18 | Continuous Delivery is a broad multifaceted subject, that sits on top of Trunk-Based Development as a practice. This 19 | website and this page, in particular, is not going to give it justice. Head on over to 20 | `ContinuousDelivery.com`{{< ext url="https://continuousdelivery.com" >}} and understand too that "lean experiments" are the part 21 | of CD and not so much the concern of Trunk-Based Development. 22 | {{< /warning >}} 23 | 24 | ## Continuous Deployment 25 | 26 | **An automatic push all the way into production; Maybe every commit** 27 | 28 | This is an extension of 'Continuous Delivery', but the deployment is to production. Certain types of startups like 29 | Netflix, Etsy and GitHub deploy their major application to production with each commit. Companies that have 30 | applications/services where clients/customers could lose money are much less likely to *firehose into production*. 31 | 32 | # References elsewhere 33 | 34 | show references 35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
2010, Jez Humble's Continuous Delivery portal
ContinuousDelivery.com
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
5 Jan 2015, TheGuardian newspaper on their CD
Delivering Continuous Delivery, continuously
53 |
54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /content/continuous-delivery/pipelines2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/continuous-delivery/pipelines2.png -------------------------------------------------------------------------------- /content/continuous-integration/ci_types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/continuous-integration/ci_types.png -------------------------------------------------------------------------------- /content/continuous-integration/ci_types2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/continuous-integration/ci_types2.png -------------------------------------------------------------------------------- /content/continuous-integration/pipelines1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/continuous-integration/pipelines1.png -------------------------------------------------------------------------------- /content/continuous-review/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-06-05T20:10:46+01:00 3 | title: Continuous Code Review 4 | weight: 65 5 | --- 6 | 7 | ## The high bar today 8 | 9 | Continuous Code Review is where the team commits to processing proposed commits (to trunk) from teammates' trunk speedily. 10 | 11 | The idea is that a system (the code portal probably) allows developers to package up commits for code review and get 12 | that in front of peers quickly. And that peer developers make a commitment to do code reviews objectively and quickly. 13 | 14 | There is a cost to multi-tasking, so maybe someone in the dev team who is between work items at that moment should focus 15 | on the review before they start new work. With a continuous review ethos, it is critical that code reviews are not 16 | allowed to back up. 17 | 18 | Companies doing Extreme Programming, often allow that pair of eyes to count as a review. Some companies require 19 | multiple reviews of code. For "the pair as reviewers too" scenario, one might have been enough and that commit will 20 | land in the trunk, without others looking at it. Five minutes and 20 seconds into Guido van Rossum's famous 2006 Mondrian 21 | presentation, he states "code review is a best alternative to pair programming", and that it is "basically 22 | asynchronous pair-programming". 23 | 24 | ### Pull Requests (PRs) 25 | 26 | The pull-request (PR) model introduced by GitHub is the dominant code review model today. The concept was available 27 | from GitHub's launch in 2008 and has revolutionized both open source and enterprise software development. Google were 28 | secretly doing the same thing with custom tooling around their Perforce install from about 2005, and Guido's 29 | presentation on Mondrian in 2006 (as mentioned) leaked that to the world (see below). 30 | 31 | A PR is one 32 | or more commits towards a goal described in an accompanying piece of text. The act of creating the PR from the branch 33 | signals the end (or a pause) in work, and the wish for the reviewers to get busy (and the CI daemon to wake up and 34 | build/verify the branch). There are caveats though. 35 | 36 | #### Open Source contributions via PRs 37 | 38 | These can come from anyone who has an account on GitHub (or equivalent). They will have forked your repository and the 39 | PR will be about commits that would come back to your repository. They may delete their repository after you consume 40 | their commits. If these are unsolicited you may well 41 | take your time reviewing them. Indeed you may never consume them, if you don't like them. Hardly continuous, but open 42 | source is mostly a volunteer activity. 43 | 44 | #### PRs from colleagues 45 | 46 | Regardless of branching model, the wish is for the PR to be reviewed fairly quickly. On GitHub (and possibly others) the PR 47 | can come from a fork or a branch in the main repo. There is little difference to the processing of these. In Trunk-Based 48 | Development teams, the PR should be on a [short-lived feature branch](/short-lived-feature-branches/) and processed very 49 | quickly by reviews towards merging back to trunk/main. A few minutes for the review is best, and tens of minutes 50 | acceptable. More than a hour or two, and you are negatively affecting cycle times. 51 | 52 | The short-lived feature branch may have received many commits before the developer initiated the pull request. Some 53 | developers will squash (rebase) the changes into a single commit before starting code review. Some teams have a policy 54 | in favor of or against squash/rebase. 55 | 56 | {{< note title="Common Code Owners" >}} 57 | Commits being reviewed are never rejected for "Only I am allowed to change source in this package" reasons. Rejections 58 | must be for objective and published reasons. 59 | {{< /note >}} 60 | 61 | ## Enterprise code review - as it was 62 | 63 | In enterprises, if code review was done at all prior to 2008, it was done in a batch, and probably a group activity. 64 | It was often abhorred as it gave a lead developer/architect a moment to set an agenda, round on a large portion of the 65 | attendees and make sure that their own code flubs were not discussed at all. 66 | 67 | Historically, open source teams never had the luxury of procrastinating about code review. They either did code reviews 68 | as they went (perhaps days were the review cadence, not hours or minutes), or they did not bother at all. 69 | 70 | ## See also 71 | 72 | See [Game Changers - Google's Mondrian](/game-changers/index.html#google-s-internal-devops-2006-onwards) and 73 | [Game Changers - GitHub's Pull Requests](/game-changers/index.html#github-s-entire-platform-2008-onwards) for the industry impact of continuous code 74 | review. 75 | 76 | # References elsewhere 77 | 78 | show references 79 | 80 |
81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
13 Nov 2014, Blog Article
Code Review - the unit of work should be a single commit
89 |
90 | -------------------------------------------------------------------------------- /content/contributions/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2018-01-01T21:07:33+01:00 3 | title: Contributions 4 | weight: 151 5 | --- 6 | 7 | Paul Hammant would like to acknowledge the contributions of a few people to the content of this site/book: 8 | 9 | 1. CD expert and friend Steve Smith - contributions to various pages. 10 | 2. TODO -------------------------------------------------------------------------------- /content/deciding-factors/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-02-01T20:08:11+01:00 3 | title: Deciding factors 4 | weight: 21 5 | --- 6 | 7 | ## Release cadence 8 | 9 | There are many factors that put pressure on the team to lengthen the interval between releases. Here are some. 10 | 11 | ### Iteration length 12 | 13 | Different Agile teams focus on different iteration lengths. Some teams work at three-week iterations, some two, 14 | and some one. Some teams do not have an iteration at all - particularly teams doing Continuous Delivery. 15 | 16 | If you are on a four week, or more iteration length, and each of those four weeks varies with proximity to the 17 | release and cannot change that you may be in a bind. You may be able to follow the tenets of Trunk-Based Development, 18 | benefit from a Continuous Integration daemon (as all branching models can), but you are not going to be able to 19 | get all the way to Continuous Delivery (or Continuous Deployment). 20 | 21 | ### Waterfall 22 | 23 | This one is easy. If you are doing waterfall, you are not close at all to the "do not break the build" mantra required 24 | to do Trunk-Based Development. Consider a short-iteration Agile methodology like Extreme Programming. 25 | 26 | ### Story size 27 | 28 | Trunk-Based Development needs you to have small stories/tasks. Optimal is you starting work on a change, should only be a matter 29 | of hours before completing and pushing it forward for code review. Longer than a couple of days, and there is going to be 30 | pressure to group a bunch of developers on a non-trunk branch and merge back later. Or worse, have developers make 31 | branches/forks from your in-progress branch. Or worse still, take intermediate merges from your branch, despite your 32 | change being incomplete. 33 | 34 | Generally speaking, the whole development team should do whatever it can to break stories/tasks into smaller stories/tasks. 35 | In Agile, there is an INVEST mnemonic{{< ext url="https://en.wikipedia.org/wiki/INVEST_(mnemonic)" >}} that aids in the splitting 36 | up of stories. 37 | 38 | ### Build times 39 | 40 | Keeping build times short is important in that it directly drives the number of commits a developer can do in a day. 41 | If the build time is a couple of minutes, developers are likely to keep a high pace. If the build time is 30 minutes or 42 | worse, developers change pace to match only a couple of commits a day and drop their throughput. 43 | 44 | ## VCS Technology Choice 45 | 46 | Your VCS/source-control technology choice should facilitate update/pull/sync from the team's trunk many times 47 | a day. The elapsed time for the update/pull/sync should be less than three seconds for the situation where you 48 | already had latest of everything. It should be no more than fifteen seconds the case of the shared trunk being ahead 49 | of you. 50 | 51 | Older versions of ClearCase and PVCS Dimensions would be 30 minutes for the former and 45 minutes for the latter. 52 | Double that if two team-mates were simultaneously trying to do the update/pull/sync operation. In that configuration, it 53 | was completely impossible for teams to practice Trunk-Based Development. 54 | 55 | ### Binaries in the Repo? 56 | 57 | Depending on how many and how often they update, some SCM/VCS/source-control technologies are better than others. 58 | Perforce can handle terabytes of binaries and textual source. Subversion aims to. Git can only do large binaries if 59 | configured in Git-LFS mode{{< ext url="https://git-lfs.github.com/" >}}. 60 | 61 | ### Repo size? 62 | 63 | It is suggested that Git and Mercurial really should not have a history (ignoring Git-LFS) that exceeds 1GB. There are field reports of clones being 64 | many times bigger than that and still working, but the development team suggests 1GB as the top limit. In order to use Git 65 | and push through that ceiling yearly, you might be in a situation where you have to keep archiving a repository, and starting 66 | a new one with no history to have more head room. Archiving might look like renaming the repository in GitHub, and turning it 67 | read-only so that all the history, issues, and code review comments are intact. Simpler clone-rationalization strategies might 68 | include recommending a "--shallow-since" date on cloning, or leveraging more recent partial clone capabilities to clone the full repo 69 | commit history without getting historical blobs until they are needed. 70 | 71 | 72 | ### Peak commit frequency 73 | 74 | In "pure" Git, if a colleague beat you to a commit/push on a branch (their code-review and automated CI passed) when you 75 | thought you were going to push, Git will inform you that you have to pull first. You pull, and you resolve your merge clashes 76 | (hopefully none), and then push again. On highly-active repos, you might struggle to find a window open long enough to push in 77 | this way without encountering the same problem. This is known as the "race to push". 78 | 79 | Fork-based "pull requests" and similar branch-based "merge requests" in hosted git services solve this to a degree, with robots 80 | keeping pull-request branches abreast of `origin:main` automatically as long as no conflicts arise. 81 | 82 | If you are using GitHub as your repository host, there is a tool that can help solve the "race to push" problem. It is called Bors-NG{{< ext url="https://github.com/bors-ng/bors-ng" >}} and it is a merge bot for GitHub pull requests. It will take care of merging the latest trunk version into your branch, running all needed tests and merging the result back into the trunk, managing this as a queue and removing these race conditions. 83 | 84 | Even with Pull Requests, however, very high commit frequencies to the shared repo means contention and an artificial 85 | serialization. Microsoft acknowledged this as one 86 | of the motivations to their Git Virtual File System (~~GitVFS~~ ~~GVFS~~ VFS for Git{{< ext url="https://github.com/Microsoft/VFSForGit" >}} which is Windows only for now). 87 | 88 | {{< quote title="Git has critical serialization points that will cause a queue to back up badly" >}} 89 | — Brian Harry
90 | Refer to Brian's "More on GVFS" blog entry 91 | {{< /quote >}} 92 | 93 | We're sure that within a few years, Git will be able to handle huge scale too. Whether with the Microsoft technologies, or 94 | something else. 95 | 96 | ## Conway's Law 97 | 98 | The organization creates applications and services that reflect the organization's own structure{{< ext url="https://en.wikipedia.org/wiki/Conway's_law" >}}. 99 | If your organization feels like this, and a Monorepo does not feel right, then MicroServices could be the direction for you. 100 | 101 | ## Database migrations 102 | 103 | In order to manage database schemas in a Trunk-Based Development way you will need to find a way to handle table-shape changes under source control, and even 104 | manage existing data where new/changed columns have happened. Pramod Sadlage and Scott Amber's book 105 | "Refactoring Databases: Evolutionary Database Design"{{< ext url="https://www.amazon.com/Refactoring-Databases-Evolutionary-paperback-Addison-Wesley/dp/0321774515" >}} 106 | goes into that much more, as does the [Continuous Delivery](/continuous-delivery/) book. 107 | 108 | ## Shared code 109 | 110 | Trunk-Based Development teams typically have common code ownership rules around contributions to different parts 111 | of the source tree. If they do not have a fully egalitarian system, they have objective rules for contributions to the tree. 112 | Rules that focus on standards and come with a promise of a prioritized and fair code review. Trunk-Based Development 113 | teams might have fine-grained write permissions for directories within the trunk, but **never** have any impediment 114 | to reading files in the trunk - everyone can see everything. 115 | 116 | 139 | -------------------------------------------------------------------------------- /content/expanding-contracting-monorepos/car_segway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/expanding-contracting-monorepos/car_segway.png -------------------------------------------------------------------------------- /content/expanding-contracting-monorepos/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-11-02T20:08:11+01:00 3 | title: Expanding Contracting Monorepos 4 | weight: 112 5 | --- 6 | 7 | At some point with a monorepo approach to source control (especially with binary dependencies in the source tree), your checkouts 8 | could be bigger than your local workstation's hard drive. Or even if the checkout is not too big for your hard drive, 9 | then it might be too much for your IDE, and you do not want to have to abandon it for Vim/Emacs. Or maybe it is not your IDE that 10 | chokes but rather is something about the build that's too much locally, despite command-line arguments to attempt to 11 | pare it down for a shorter elapsed time. 12 | 13 | There is a way to intelligently expand or contract the checkout on your developer workstation, to alleviate all of 14 | the above. 15 | 16 | ## Gcheckout.sh 17 | 18 | Google's in-house DevOps uses some simple scripting to 19 | modify the checkout on the developer's workstation to omit the source files/packages that are not needed for the 20 | current intentions of the developer. This Blaze related technology is a shell command called 'gcheckout'. It can modify the mappings between the multi-gigabyte HEAD 21 | revision of company-wide trunk (monorepo) and developer's own workstation. Thus the source-control tools maintain the 22 | **smallest possible subset** of the monorepo on the developer's workstation, for them to perform their daily work. 23 | Google and the industry refer to the general feature as 'sparse checkout'. 24 | 25 | You can run `gcheckout` at any time to modify your sparse checkout to be bigger or smaller (or wholly different) for 26 | different reasons. All of those are operations on your local representation of a larger trunk. 27 | 28 | ## Contrived example of use 29 | 30 | We detailed two intentions for directed graph build systems above, using a contrived application. Here is one more: 31 | 32 | * I now want to change `TheORMweBothDependOn`, because a change to `MyTeamsApplication` requires me to do that. 33 | 34 | In Google, rather than feed into the backlog of the team that maintains `TheORMweBothDependOn` (which may exist as a part-time 35 | committee rather than a team), the developer in question would make the change themselves. Perhaps they had made 36 | it in the same commit as the first usage of it for `MyTeamsApplication`. In the code review cycle (Google practices 37 | common code ownership), the approvers for the `TheORMweBothDependOn` would see all the changes together. The larger change is 38 | all accepted or rejected (to be remediated) atomically. 39 | 40 | So our developer was working on `MyTeamsApplication`, which depended on `TheORMweBothDependOn` (which probably transitively 41 | depended on other things). Now that developer is going to change `TheORMweBothDependOn` and that impacts `TheirApplication` 42 | too. The Blaze related checkout-modifying technology 'gcheckout' performs an expansion to bring in `TheirApplication` to the 43 | developer's checkout. From that moment on, the developer doing update/pull/sync will bring down minute by minute 44 | changes to those three modules. For free, the build expands to make sure that the `TheORMweBothDependOn` changes do not 45 | break either of `MyTeamsApplication` or `TheirApplication`. 46 | 47 | ### Directory structure and working copy. 48 | 49 | If I ran `gcheckout` with MyTeamsApplication+TheirApplication as the parameter I would get working copy that looked like: 50 | 51 | ```txt 52 | root/ 53 | java/ 54 | BUILD 55 | com/ 56 | BUILD 57 | google/ 58 | BUILD 59 | myteamsapplication/ 60 | BUILD 61 | MyTeamsApplication.java // and hundreds of other packages and source files 62 | theirapplication/ 63 | BUILD 64 | TheirApplication.java // and hundreds of other packages and source files 65 | theormwebothdependon/ 66 | BUILD 67 | TheORMweBothDependOn.java // and hundreds of other packages and source files 68 | java_test/ 69 | BUILD 70 | com/ 71 | BUILD 72 | google/ 73 | BUILD 74 | myteamsapplication/ 75 | BUILD 76 | MyTeamsApplicationTest.java // and hundreds of other packages and source files 77 | theirapplication/ 78 | BUILD 79 | TheirApplicationTest.java // and hundreds of other packages and source files 80 | theormwebothdependon/ 81 | BUILD 82 | TheORMweBothDependOnTest.java // and hundreds of other packages and source files 83 | ``` 84 | 85 | If I ran `gcheckout` with MyTeamsApplication as the parameter I would get working copy that looked like: 86 | 87 | ```txt 88 | root/ 89 | java/ 90 | BUILD 91 | com/ 92 | BUILD 93 | google/ 94 | BUILD 95 | myteamsapplication/ 96 | BUILD 97 | MyTeamsApplication.java // and hundreds of other packages and source files 98 | theormwebothdependon/ 99 | BUILD 100 | TheORMweBothDependOn.java // and hundreds of other packages and source files 101 | java_test/ 102 | BUILD 103 | com/ 104 | BUILD 105 | google/ 106 | BUILD 107 | myteamsapplication/ 108 | BUILD 109 | MyTeamsApplicationTest.java // and hundreds of other packages and source files 110 | theormwebothdependon/ 111 | BUILD 112 | TheORMweBothDependOnTest.java // and hundreds of other packages and source files 113 | ``` 114 | 115 | If I ran `gcheckout` with TheORMweBothDependOn as the parameter I would get working copy that looked like: 116 | 117 | ```txt 118 | root/ 119 | java/ 120 | BUILD 121 | com/ 122 | BUILD 123 | google/ 124 | BUILD 125 | theormwebothdependon/ 126 | BUILD 127 | TheORMweBothDependOn.java // and hundreds of other packages and source files 128 | java_test/ 129 | BUILD 130 | com/ 131 | BUILD 132 | google/ 133 | BUILD 134 | theormwebothdependon/ 135 | BUILD 136 | TheORMweBothDependOnTest.java // and hundreds of other packages and source files 137 | ``` 138 | 139 | You can keep rerunning the `gcheckout` to expand or contract your working copy to meet your current goals. 140 | 141 | ## Contrived example of use #2 142 | 143 | We used 'change the wheel on a car', on the [Branch By Abstraction](/branch-by-abstraction/) page for its contrived 144 | example. It will serve us again here. The wheel is what we want to change. The other team using 'Wheel(s)' is making a 145 | Segway thing (two wheels and self-balancing via high-torque and very responsive motors). Here's the procedure: 146 | 147 | ![](car_segway.png) 148 | 149 | The starting position is two teams working separately, using 'Wheel' (4 for cars, 2 for Segways). Without any commits 150 | happening the engineer changing 'Wheel' for everyone, runs gcheckout (or its equivalent) to modify the source in the 151 | IDE to the union of Car and Segway (and in-house dependencies). That is marked as step 0. Let us say the 152 | change is quick/easy this time (not requiring Branch By Abstraction) step 1 shows the single commit that changes 153 | the wheel implementation for everyone. After the commit/push, running again shows the application focused team checkout - either 154 | 'Car' or 'Segway'. 155 | 156 | ## Git's Sparse checkouts 157 | 158 | Git has a 'sparse checkout' capability, which exactly facilitates this sort of thing. Subversion and Mercurial do too. 159 | Perforce has a 'client spec' capability that is more or less the same. A team wanting to have their own gcheckout equivalent 160 | would have some scripting around sparse checkouts (or equivalent). 161 | 162 | ### Using Git this way today 163 | 164 | If you're willing to go a 'split history' maneuver on your monorepo once or twice a year, Git can do the expandable and 165 | contractible monorepo setup today. 166 | 167 | ## Perforce's client-specs 168 | 169 | Perforce has a 'client spec' (alternatively 'view') that is accessed via the client command or UI. Amongst other things, it 170 | allows a checkout to be a subset of the directories/files available within the branch. A list of globbed includes and 171 | excludes is the format. You would script this (as Google did until 2012) to have a directed graph driven 172 | expandable/contractible checkout. 173 | 174 | ## PlasticSCM's cloaked.conf 175 | 176 | As Perforce, but via 'cloaked.conf' file. 177 | 178 | ## Subversion's sparse-checkouts 179 | 180 | Subversion has a 'sparse checkout' capability. You do a series of checkout operations at various directory levels in order 181 | to create the mapping, so is less atomic or centrally configured than the others. They have made also made a 'viewspec' script 182 | {{< ext url="http://svn.apache.org/repos/asf/subversion/trunk/tools/client-side/svn-viewspec.py" >}} 183 | to allow the reshaping of the working copy to happen in a more declarative way. 184 | -------------------------------------------------------------------------------- /content/feature-flags/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-04-01T20:08:11+01:00 3 | title: Feature flags 4 | weight: 41 5 | --- 6 | 7 | Feature Flags are a time-honored way to control the capabilities of an application or service in a large decisive way. 8 | 9 | ### An Example 10 | 11 | Say you have 12 | an application or service that launches from the command-line that has a `main` method or function. Your feature flag 13 | could be `--withOneClickPurchase` passed in as a command-line argument. That could activate lines of code in the app to 14 | do with Amazon's patented one-click purchasing 15 | experience. Without that command-line argument, the application would run with a shopping cart component. At least 16 | that's the way the developers coded that application. The 'One Click Purchase' and 'Shopping Cart' alternates are 17 | probably also the same language that the business people associated with the project use. It gets complicated in 18 | that flags need not be implicitly a/b or new/old, they could be additive. In our case here, there could also be a 19 | `--allowUsersToUseShoppingCartInsteadOfOneClick` capability. Flags can be additive, you see. 20 | 21 | {{< note title="Flags Are Toggles" >}} 22 | Industry Luminary, Martin Fowler, calls this technique 'Feature Toggles', and wrote a foundational definition (see refs below). 23 | Feature Flags is in wider use by the industry, though, so we're going with that. 24 | {{< /note >}} 25 | 26 | ## Granularity 27 | 28 | It could be that the flag controls something large like the UI of a component. In our case above we could say that 29 | `OneClickPurchasing` and `ShoppingCart` are the names of components. It could be that the granularity of the flag 30 | is much smaller - Say Americans want to see temperatures in degrees Fahrenheit and other nationalities would 31 | prefer degrees Centigrade/Celsius. We could have a flag `--temp=F` and `--temp=C`. For fun, the developers also added 32 | `--temp=K` (Kelvins). 33 | 34 | ## Implementation 35 | 36 | For the `OneClickPurchasing` and `ShoppingCart` alternates, it could be that a `PurchasingCompleting` 37 | abstraction was created. Then at the most primordial boot place that's code controlled, the `--withOneClickPurchase` flag 38 | is acted upon: 39 | 40 | Java, by hand: 41 | 42 | ```java 43 | if args.contains("--withOneClickPurchase") { 44 | purchasingCompleting = new OneClickPurchasing(); 45 | } 46 | ``` 47 | 48 | Java Dependency Injection via config: 49 | 50 | ```java 51 | bootContainer.addComponent(classFromName(config.get("purchasingCompleting"))); 52 | ``` 53 | 54 | There are many more ways of passing flag intentions (or any config) to a runtime. If you at all can, you want to 55 | avoid if/else conditions in the code where a path choice would be made. Hence our emphasis on an abstraction. 56 | 57 | ## Continuous Integration pipelines 58 | 59 | It is important to have CI guard your reasonable expected permutations of flags. That means tests that happen on an 60 | application or service after launching it, should also be adaptable and test what is meaningful for those flag 61 | permutations. It also means that in terms of CI pipelines there is a fan-out **after** unit tests, for each meaningful 62 | flag permutation. A crude equivalent is to run the whole CI pipeline in parallel for each meaningful flag permutation. 63 | That would mean that each commit in the trunk kicks off more than one build - hopefully from elastic 64 | infrastructure. 65 | 66 | ## Runtime switchable 67 | 68 | Sometimes flags set at app launch time is not enough. Say you are an Airline, selling tickets for flights online. 69 | You might also rent out cars in conjunction with a partner - say 'Really Cool Rental Cars' (RCRC). The connection to 70 | any partner or their up/down status is outside your control, so you might want a switch in the software that works 71 | without relaunch, to turn "RCRC partner bookings" on or off, and allow the 24/7 support team to flip it if certain 'Runbook' conditions 72 | have been met. In this case, the end users may not notice if Hertz, Avis, Enterprise, etc are all still amongst 73 | the offerings for that airport at the flight arrival time. 74 | 75 | Key for Runtime switchable flags is the need for the state to persist. A restart of the application or service should 76 | not set that flag choice back to default - it should retain the previous choice. It gets complicated when you think 77 | about the need for the flag to permeate multiple nodes in a cluster of horizontally scaled sibling processes. For 78 | that last, then holding the flag state in Consul{{< ext url="https://www.consul.io" >}}, 79 | Etcd{{< ext url="https://github.com/coreos/etcd" >}} (or equivalent) is the modern way. 80 | 81 | ## Build Flags 82 | 83 | Build flags affect the application or service as it is being built. With respect to the `--withOneClickPurchase` flag again, 84 | the application would be incapable at runtime of having that capability if the build were not invoked with the suitable 85 | flag somehow. 86 | 87 | ## A/B testing and betas 88 | 89 | Pushing code that's turned off into production, allows you to turn it on for ephemeral reasons - you want a subset of 90 | users to knowingly or unknowingly try it out. A/B testing (driven by marketing) is possible with runtime flags. So is 91 | having beta versions of functionality/features available to groups. 92 | 93 | ## Tech Debt - pitfall 94 | 95 | Flags get put into codebases over time and often get forgotten as development teams pivot towards new business deliverables. 96 | Of course, you want to wait a while until it is certain that you are fixed on a toggle state, and that's where the 97 | problem lies - the application works just fine with the toggle left in place, and the business only really cares 98 | about new priorities. The only saving grace is the fact that you had unit tests for everything, even for code that 99 | is effectively turned off in production. Try to get the business to allow the remediation of flags (and the code 100 | they apply to) a month after the release. Maybe add them to the project's readme with a "review for delete" date. 101 | 102 | ## History 103 | 104 | Some historical predecessors of feature toggles/flags as we know it today: 105 | 106 | - Unified Versioning through Feature Logic (Andreas Zeller and Gregor Snelting, 1996){{< ext url="http://www.cs.tufts.edu/~nr/cs257/archive/andreas-zeller/tr-96-01.pdf" >}} - white paper. 107 | - Configuration Management with Version Sets: A Unified Software Versioning Model and its Applications (Andreas Zeller's, 1997){{< ext url="https://www.st.cs.uni-saarland.de/publications/files/zeller-thesis-1997.pdf" >}} - Ph.D. thesis. 108 | 109 | There's a warning too: 110 | 111 | - "#ifdef considered harmful" (Henry Spencer and Geoff Collyer, 1992){{< ext url="http://www.literateprogramming.com/ifdefs.pdf" >}} - white paper. 112 | 113 | Brad Appleton says: 114 | 115 |
116 | The thing I do not like about feature-toggles/flags is when they end up NOT being short-lived as intended, 117 | and we end up having to revisit Spencer and Collyer's famous paper. The funny thing is feature-branches 118 | started out the same way. When they were first introduced it was for feature-teams using very large features, and the 119 | purpose of the separate branches was because too many people were trying to commit at the same time to the same branch. 120 | So the idea was use separate branches (for scale) and teams would integrate to their team-branch daily or more often 121 | WITH at least nightly integration across all feature-branches [sigh]. 122 |
123 | 124 | # References elsewhere 125 | 126 | show references 127 | 128 |
129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 |
29 Oct 2010, MartinFowler.com article
Feature Toggle
137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
30 May 2011, TechCrunch article
The Next 6 Months Worth Of Features Are In Facebook's Code Right Now, But We Can't See
145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 |
19 Jun 2013, Slides from a talk
Branching Strategies: Feature Branches vs Branch by Abstraction
153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 |
10 Oct 2014, Conference Talk
Trunk-Based Development in the Enterprise - Its Relevance and Economics
161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 |
08 Feb 2016, MartinFowler.com article
Feature Toggles
169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 |
23 May 2017, DevOps.com article
Feature Branching vs. Feature Flags: What’s the Right Tool for the Job?
177 | 178 |
179 | -------------------------------------------------------------------------------- /content/game-changers/away_from_tbd1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/game-changers/away_from_tbd1.png -------------------------------------------------------------------------------- /content/game-changers/away_from_tbd2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/game-changers/away_from_tbd2.png -------------------------------------------------------------------------------- /content/game-changers/away_from_tbd3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/game-changers/away_from_tbd3.png -------------------------------------------------------------------------------- /content/game-changers/beneficial_to_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/game-changers/beneficial_to_both.png -------------------------------------------------------------------------------- /content/game-changers/google-tbd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/game-changers/google-tbd.png -------------------------------------------------------------------------------- /content/game-changers/mondrian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/game-changers/mondrian.png -------------------------------------------------------------------------------- /content/game-changers/to_tbd1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/game-changers/to_tbd1.png -------------------------------------------------------------------------------- /content/game-changers/to_tbd2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/game-changers/to_tbd2.png -------------------------------------------------------------------------------- /content/game-changers/to_tbd3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/game-changers/to_tbd3.png -------------------------------------------------------------------------------- /content/game-changers/to_tbd3_but_secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/game-changers/to_tbd3_but_secret.png -------------------------------------------------------------------------------- /content/ix_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/ix_key.png -------------------------------------------------------------------------------- /content/key/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2017-03-01T20:08:11+01:00 3 | title: Key to branch diagrams 4 | weight: 141 5 | --- 6 | 7 | ![](key.png) 8 | -------------------------------------------------------------------------------- /content/key/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/key/key.png -------------------------------------------------------------------------------- /content/observed-habits/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-08-01T09:42:02+05:00 3 | title: Observed habits 4 | weight: 81 5 | --- 6 | 7 | ## No Code Freeze 8 | 9 | Developers living in a Trunk-Based Development reality, mostly do not experience variance in their days or weeks on the 10 | trunk. In particular, there is no "we're close to a release so let's freeze code", and generally there is no indication 11 | of a slowdown in proximity to a release. Sure, a couple of developers out of a team might be assigned to bug-fixing closer 12 | to the release but everyone else is going to work at full speed. 13 | 14 | Generally speaking, the trunk is a place to firehose commits into, and the habits of the developers are such that 15 | everything is ready to go live. If a team is doing 12 releases a year, then a release branch that is cut on the just in time 16 | basis and is the one that is observed to be 'frozen' because of the absence of developers. Refer [branch for release](/branch-for-release/). 17 | 18 | ### Every Day is the same 19 | 20 | Ignoring meetings, developers commit/push at the same rate and to the same place regardless of the day of the week or 21 | the week of the month. This is a reinforcement of the "No Code Freeze" rule above. Sure a small subset of the team 22 | may focus on an imminent release (bug fixes in the trunk, cherry-picked to the release branch), but the majority 23 | should be focusing on 'business as usual' implementation of functionality for a future release. 24 | 25 | ## Quick Reviews 26 | 27 | Teams doing Trunk-Based Development know that their commits/pushes will be scrutinized by others, as soon 28 | as they have landed on the shared trunk. They are keen on bringing that forward, not delaying it, so they may prefer to 29 | pair-program on code changes. Or they may ask colleagues for a code review at the time the change is submitted to be 30 | merged into the trunk. 31 | 32 | ## Chasing HEAD 33 | 34 | Trunk-Based Development teams update/pull/sync from the shared trunk often. Many times a day in fact. 35 | 36 | ## Running the build locally 37 | 38 | Developers practicing Trunk-Based Development run the build before a commit/push in order to not break the build. 39 | This one practice, for very small teams, allows them to not set up a CI server until later. If they cannot push their 40 | commits to the shared trunk because someone else beat them to it then they have to do another update/sync/pull and 41 | another build and push their revised commit(s). "It worked on my machine" says the developer that does not 42 | want to confess to breaking the build (assuming quick reliable idempotent builds). 43 | 44 | ## Facilitating commits 45 | 46 | Because everyone in a team is chasing HEAD of the trunk, there could be a moment where one change is imagined that 47 | could inconvenience everyone else in some way. Say a big directory rename as part of an extensive rework of the 48 | source with it. It could be that the developer performing those two changes does the rename first and pushes it through to 49 | the trunk where everyone will get it on the next pull/sync/update. It might be that if separated somehow, the rename 50 | on its own is easier to consume by teammates, with the second being a relatively smaller commit later. 51 | 52 | Git and Mercurial track files through their content rather than by directory and file name, so they make light of the 53 | situation described above, anyway. 54 | 55 | ## Powering through broken builds 56 | 57 | So because of that lazy developer, or the flaky build, or pure accident of timing (Google has a commit every 30 58 | seconds into their monorepo - there must be quantum entangled commits on a 0.0001% basis), the trunk will be observed 59 | to be broken occasionally. 60 | 61 | The best implementations are going to perform automatic rollback of a broken commit that lands in the trunk. The 62 | developer gets notified and they get to fix it quietly on their workstation. 63 | 64 | A developer wanting to update/pull/sync from the shared trunk often runs the risk of encountering that 65 | statistically improbable broken build. They do not want to have the commits that broke the trunk, on their workstation 66 | if they are developing. So what they do is update/pull/sync to the last known good commit, and only go further 67 | ahead when the trunk build is officially repaired. This way they know they can stay 'green' on their workstation. Some 68 | companies engineer a system where the last known good commit hash/number is stored in a network share, and a shell 69 | script used for update/pull/sync does so to that instead of HEAD revision. 70 | 71 | ### Build Cop 72 | 73 | If the Continuous Integration server is batching commits to trunk in each build, or the elapsed time for a build is 74 | long then a "build cop" role might be required within the team to help sort out build breakages. Sadly that 75 | means that locking the trunk to prevent further checkins on top of the broken one might be necessary as some form 76 | of bisecting is performed to work out which commit broke the build and should be rolled back. Obviously a Continuous 77 | Integration server setup that can run one build per commit is best. 78 | 79 | ## Shared Nothing 80 | 81 | Developers on their workstations rely on a 'microcosm' setup for the application or service 82 | they are developing. They can: 83 | 84 | * bring up the application on their workstation and play with it. 85 | * run all unit, integration and functional tests against it locally 86 | 87 | Shared nothing requires significant discipline to achieve. It generally means that no TCP-IP leaves the developers 88 | box, and being able to prove that by running those operations while disconnected from the network. The 89 | implementing of the wire mocking (service virtualization) of dependent tiers outside the team, is a given. The highest 90 | accomplished Trunk-Based Development teams employ mocking of tiers within the same application, in order to make 91 | tests fast and stable. Technologies such as Mountebank{{< ext url="http://www.mbtest.org" >}} make 92 | programming working with wire mocking easy. Tiers refer to a layer-cake view of an applications construction, of course. 93 | 94 | With a Microcosm strategy which delivers shared nothing for a developer workstation, it is acknowledged that 95 | non-functional consistency with production has been thrown out of the window and that only functional correctness 96 | is being honored. This is only really any good for the act of development on a workstation, and the verification of 97 | that (per commit) by a Continuous Integration daemon. 98 | 99 | Your team will need many named QA environments, and many named 100 | user acceptance testing (UAT) environments. Each of those with different rules about the frequency of deployment, and even perhaps even 101 | a temporarily reservation for different reasons. Those environments pull together **real** dependent services 102 | and integrated applications. As much as possible those environments should not have shared services. 103 | 104 | Companies often make a classic mistake when buying software in that they (say) buy one license for prod, and another 105 | for all dev, QA, and UAT, meaning the DevOps team had configured it as shared for all those environments, with a 106 | wide-ranging negative impact on productivity and quality for innumerable and sometimes subtle psychological reasons. 107 | 108 | ## Common code ownership 109 | 110 | Committing to the trunk many times a day requires a broad sense of ownership to code, and a willingness to allow 111 | developers to contribute changes to sections of an application or service that they have not previously be involved 112 | with. This privilege does come with responsibilities and checks. The former is to standards, and the checks are by the CI server, 113 | and by humans who should honor to do a speedy code review. That last, for the highest performing teams, means as soon 114 | as the proposed commit is ready. 115 | 116 | ## Always Release Ready 117 | 118 | Not only do developers practicing Trunk-Based Development not break the build with any commit, they also sign up to 119 | being able to go live at short notice. For example, one hour, if the CIO visits and says it is going to happen. That 120 | means there is a bunch of automated tests that come with the build. 121 | 122 | ## Thin vertical slices 123 | 124 | Where possible stories or tasks that have been pulled from the backlog should be achievable by a developer or pair of 125 | developers in a short period of time, and in a small number of commits. They should also transcend all the apparent 126 | tiers of the stack, and not have to jump between developers with specialized knowledge in order to be able to 127 | completed. The Agile industry donates the INVEST{{< ext url="https://en.wikipedia.org/wiki/INVEST_(mnemonic)" >}} principle 128 | as well as "Thin Vertical Slices"{{< ext url="http://www.scruminc.com/wp-content/uploads/2015/06/User-Stories-2.0.pdf" >}} 129 | for this purpose, and that are great enablers of high throughput commits to the trunk, and always being release ready. 130 | -------------------------------------------------------------------------------- /content/publications/2015_state_of_devops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/publications/2015_state_of_devops.png -------------------------------------------------------------------------------- /content/publications/2016_state_of_devops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/publications/2016_state_of_devops.png -------------------------------------------------------------------------------- /content/publications/2017_state_of_devops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/publications/2017_state_of_devops.png -------------------------------------------------------------------------------- /content/publications/build_quality_in.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/publications/build_quality_in.jpeg -------------------------------------------------------------------------------- /content/publications/cd-book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/publications/cd-book.png -------------------------------------------------------------------------------- /content/publications/cdit_org_perf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/publications/cdit_org_perf.png -------------------------------------------------------------------------------- /content/publications/ci.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/publications/ci.jpg -------------------------------------------------------------------------------- /content/publications/devopsHandbook.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/publications/devopsHandbook.jpg -------------------------------------------------------------------------------- /content/publications/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2017-01-01T20:08:11+01:00 3 | title: Publications 4 | weight: 131 5 | --- 6 | 7 | ## Books promoting Trunk-Based Development 8 | 9 |
10 | 11 | ## Software Configuration Management Patterns (November 14, 2003) 12 | 13 |
14 | 15 |

16 | Software Configuration Management Patterns: Effective Teamwork, Practical Integration
17 | by Stephen P. Berczuk with Brad Appleton 18 |
19 | Amazon (paperback)
20 |

21 |
22 |
23 | 24 | ## Continuous Integration (June 29, 2007) 25 | 26 |
27 | 28 |

29 | Continuous Integration: Improving Software Quality and Reducing Risk
30 | by Paul M. Duvall, Steve Matyas, Andrew Glover 31 |
32 | Amazon (hardback, kindle)
33 |

34 |
35 |
36 | 37 | ## Continuous Delivery (July 27, 2010) 38 | 39 |
40 |

Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation
41 | by Jez Humble and Dave Farley
42 | Amazon (hardback, kindle)
43 | InformIT (pdf, epub, mobi)
44 | Translations: 中文 | 日本語 | 한국말 | português

45 | 46 | ## Lean Enterprise (January 3, 2015) 47 | 48 |
49 | 50 |

51 | Lean Enterprise: How High Performance Organizations Innovate at Scale
52 | by Jez Humble, Joanne Molesky and Barry O'Reilly 53 |
54 | Amazon (hardback, kindle)
55 | O'Reilly (pdf, epub, mobi)
56 | Translations: 中文 | 57 | 日本語 | 58 | Deutsch | 59 | português 60 |

61 |
62 |
63 | 64 | ## Build Quality In (February 27, 2015) 65 | 66 |
67 | 68 |

69 | Build Quality In: Continuous Delivery and DevOps Experience Reports
70 | by Steve Smith and Matthew Skelton 71 |
72 | Leanpub (kindle) 73 |
74 |

75 |
76 |
77 | 78 | ## DevOps Handbook (October 6, 2016) 79 | 80 |
81 | 82 |

83 | The DevOps Handbook: How to Create World-Class Agility, Reliability, and Security in Technology Organizations
84 | by Gene Kim, Jez Humble, Patrick Debois, John Willis, John Allspaw 85 |
86 | Amazon (hardback, kindle) 87 |
88 | O'Reilly (pdf, epub, mobi) 89 |
90 |

91 |
92 |
93 | 94 | ## Measuring Continuous Delivery (2017) 95 | 96 |
97 | 98 |

99 | Measuring Continuous Delivery: The what, why, and how of measuring Continuous Delivery
100 | by Steve Smith 101 |
102 | Leanpub (kindle) 103 |
104 |

105 |
106 |
107 | 108 | ## Reports promoting Trunk-Based Development 109 | 110 |
111 | 112 | ## More Engineering, Less Dogma (Oct 18, 2013) 113 | 114 |
115 | 116 |

117 | More Engineering, Less Dogma: The Path Toward Continuous Delivery Of Business Value
118 | by Kurt Bittner and Glenn O’Donnell 119 |
120 | Forrester Research - link 121 |

122 |
123 |
124 | 125 | ## The Role of Continuous Delivery in IT and Organisational Performance (Oct 27, 2015) 126 | 127 |
128 | 129 |

130 | The Role of Continuous Delivery in IT and Organizational Performance
131 | by Nicole Forsgren and Jez Humble 132 |
133 | Proceedings of the Western Decision Sciences Institute - link 134 |

135 |
136 |
137 | 138 | ## 2015 State of DevOps Report 139 | 140 |
141 | 142 |

143 | A survey of thousands of software development professionals distilled into a report that determines 144 | practices for the highest achieving organizations
145 | It was gratifying, though unsurprising, to find that deployment pain was 146 | predicted by whether the key continuous delivery practices had been 147 | implemented: comprehensive test and deployment automation, the 148 | use of continuous integration including Trunk-Based Development, and 149 | version control of everything required to reproduce production environments 150 |
by Puppet Labs 151 |
152 | Puppet's download form for the report 153 |

154 |
155 |
156 | 157 | ## 2016 State of DevOps Report 158 | 159 |
160 | 161 |

162 | As their 2015 report, a survey of thousands of software development professionals distilled into a report that determines 163 | practices for the highest achieving organizations
164 | The idea that developers should work in small batches 165 | off master [main since 2020] or trunk rather than on long-lived feature 166 | branches is still one of the most controversial ideas 167 | in the Agile canon, despite the fact it is the norm in 168 | high-performing organizations such as Google. 169 | Indeed, many practitioners express surprise that this 170 | practice is in fact implied by continuous integration, 171 | but it is: The clue is in the word 'integration.' 172 |
by Puppet Labs 173 |
174 | Puppet's download form for the report 175 |

176 |
177 |
178 | 179 | ## 2017 State of DevOps Report 180 | 181 |
182 | 183 |

184 | As their 2016 report, a survey of thousands of software development professionals distilled into a report that determines 185 | practices for the highest achieving organizations
186 | Last year, we investigated the role that trunk-based 187 | development plays in continuous delivery. While our 188 | experience shows that developers in high-performing 189 | teams work in small batches and develop off of trunk 190 | or master [main since 2020], rather than long-lived feature branches, 191 | many practitioners in the industry routinely work in 192 | branches or forks. 193 |
by Puppet Labs 194 |
195 | Puppet's download form for the report 196 |

197 |
198 |
199 | -------------------------------------------------------------------------------- /content/publications/lean-enterprise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/publications/lean-enterprise.png -------------------------------------------------------------------------------- /content/publications/less_dogma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/publications/less_dogma.png -------------------------------------------------------------------------------- /content/publications/mcd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/publications/mcd.png -------------------------------------------------------------------------------- /content/publications/scm-patterns.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/publications/scm-patterns.jpg -------------------------------------------------------------------------------- /content/release-from-trunk/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-05-06T19:56:50+01:00 3 | title: Release from trunk 4 | weight: 56 5 | --- 6 | 7 | Teams with a very high release cadence do not need (and cannot use) release branches at all. They have to release from 8 | the trunk. 9 | 10 | ![](release_from_trunk.png) 11 | ([key](/key/)) 12 | 13 | It is most likely that such teams do not use a Dewey-decimal release numbering scheme, and instead have something 14 | referent to the commit number or date and time. They probably also choose to roll forward and fix the bug on the 15 | trunk as if it were a feature, albeit as quickly as possible. 16 | 17 | Here's what stylized commits look like: 18 | 19 | ![](release_from_trunk2.png) 20 | ([key](/key/)) 21 | 22 | No slow down around a release and bug fixes inline. 23 | 24 | Teams with one release a day (or less) **might** still make a branch, to cherry-pick the bug-fix to 25 | and release from: 26 | 27 | ![](release_from_trunk3.png) 28 | ([key](/key/)) 29 | 30 | {{< note title="Branches can be made retroactively" >}} 31 | Newbies to source-control systems often forget that you don't have to make a branch because you think you might need 32 | it in the future. For any source-control technology made today, you can choose the revision in the past to branch 33 | from. The outcome is exactly the same as if you had made it at the time. 34 | {{< /note >}} 35 | -------------------------------------------------------------------------------- /content/release-from-trunk/release_from_trunk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/release-from-trunk/release_from_trunk.png -------------------------------------------------------------------------------- /content/release-from-trunk/release_from_trunk2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/release-from-trunk/release_from_trunk2.png -------------------------------------------------------------------------------- /content/release-from-trunk/release_from_trunk3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/release-from-trunk/release_from_trunk3.png -------------------------------------------------------------------------------- /content/short-lived-feature-branches/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-06-03T20:10:46+01:00 3 | title: Short-Lived Feature Branches 4 | weight: 63 5 | --- 6 | 7 | 8 | 9 | This branching model was facilitated with the advent of very lightweight branching that came with Git and Mercurial 10 | in the mid-2000's, though there is evidence that Google were effectively doing the same in their Monorepo for some years before. 11 | 12 | Either as branching directly off main, or in a fork of the whole repository. These branches are destined to come 13 | back as "pull requests" into the main/trunk. 14 | 15 | 16 | 17 | With the Pull Request (and code review) advance, the cut-off point for team sizes that graduated from "direct to the trunk" to short lived feature branches moved lower. 18 | While it was up to 100 before Git's lightweight branching, it is now up to 15 people. With 16 or more, the team is more 19 | productive with short-lived feature branches, and corresponding CI daemons verifying those in advance of 20 | commits landing in the trunk. 21 | 22 | One key rule is the length of life of the branch before it gets merged and deleted. Simply put, the branch 23 | should only last a couple of days. Any longer than two 24 | days, and there is a risk of the branch becoming a long-lived feature branch (the antithesis of trunk-based development). 25 | 26 | 27 | 28 | Another key rule is how many developers are allowed congregate on a short-lived feature branch. Another simple answer: 29 | the developer count should stay at one (or two if pair-programming). These short-lived feature branches are not shared 30 | within a team for general development activity. They may be shared for the purposes of code review, but that is entirely 31 | different to writing production code and tests. 32 | 33 | 34 | 35 | ![](/5-min-overview/trunk_pr.png) 36 | 37 | Sometimes the community calls these 'task' or 'topic' branches, instead of long-lived feature branch. 38 | 39 | ## Merge directionality 40 | 41 | Short-lived feature branches are real branches and merge is a first class concept. In the run-up to completing work 42 | on the short-lived feature branch, you will need to bring it up to date with main (trunk). That is an effective 43 | merge whichever way you do it. Look at the branch at this moment, it may appear to be much younger than it was 44 | before that operation. The changes have to now go back to main (trunk) in another merge operation. In GitHub, for 45 | 'pull requests' (or equivalent in other platforms), the user interface may handle that last merged back for you, and even 46 | go as far as to delete the short-lived feature branch. 47 | 48 | 49 | 50 | To recap: merges to the short-lived feature branch are allowed to bring it closer to HEAD of main (trunk). Merges 51 | to main (trunk) are allowed only as part of closing out the short-lived feature branch (and just before deleting it). 52 | 53 | 54 | 55 | ## Two developers concurrently doing short-lived feature branches 56 | 57 | Say two features are being worked on concurrently: features X and Y. Both will take a day to complete, and two developers are 58 | working independently on them. Or four developers if pair-programming is that team's way. The reality of the merge back to 59 | main/trunk, before the deletion of the short-lived feature branch, is that a merge of changes **from** main/trunk 60 | is often needed, before the merge **to** main/trunk. 61 | 62 | ![](slfb_pull-push.png) 63 | 64 | Workflows include: 65 | 66 | * Attempt to merge to main/trunk and if that's blocked do a merge/pull from main/trunk before attempting the push again. 67 | * Do a speculative main/pull from main/trunk before attempting any push to main/trunk. 68 | 69 | 70 | 71 | The latter leaves no trace if there's nothing to merge in from the other branch. 72 | 73 | ### Workstations included 74 | 75 | Really though Developers work on their own workstations. That is both for their "working copy" as well as their local Git clones complete with branches: 76 | 77 | ![](slfb_working-copy.png) 78 | 79 | 80 | 81 | The white dots are just general development work (save files from the IDE), and the green dot is the same but validated by a local build that passes and a maybe a local commit. 82 | 83 | While each developer works, they maintain a local clone of the main/trunk and their own short-lived feature branch only. 84 | Indeed for a period of time, their short-lived feature branch may not yet exist in the shared origin repo. 85 | 86 | 87 | 88 | 89 | ## Personal preferences 90 | 91 | At some point, the short-lived feature branch has to be brought right up to date with main (trunk) in a merge 92 | operation before the result being merged back to trunk (and the branch deleted). There are a number of approaches 93 | for this, and while teams may have a policy, some teams leave it to personal preference for the developer. 94 | 95 | ### Git stash 96 | 97 | Some people do `git stash` before `git pull` before `git stash pop`. There's a chance that when you `pop` your 98 | working copy may be in a merge clash situation that has to be resolved before you progress. This way will always 99 | result in your change being a single commit, at the HEAD of the branch (as Subversion would always do). 100 | 101 | 102 | 103 | ### Git rebase 104 | 105 | Some people do `git rebase`. Refer to a well written Atlassian document on this {{< ext url="https://www.atlassian.com/git/tutorials/merging-vs-rebasing" >}} as well as one from ThoughtBot {{< ext url="https://robots.thoughtbot.com/git-interactive-rebase-squash-amend-rewriting-history" >}} that talks about `squash` too. Even with this model, 106 | you may encounter a merge clash, and have to resolve that locally before you can push the result anywhere, or do 107 | further merges (to `main` hopefully). 108 | 109 | 110 | 111 | ## Pitfalls 112 | 113 | Working towards a pull request can sometimes make you look past the power of streaming a series of small commits into trunk for 114 | the benefit of all. High-throughput XP teams from the end of the 90's onward would stream tens of commits a day (per pair) 115 | into the trunk. Each commit would be an incremental step forward and able to go live as is, were the team to change plans 116 | to do so. A mistake in thinking for this way of working, is one pull-request for one Agile story/card (and no more). Getting 117 | out of that mind trap would be to practice (say) a pull-request for refactoring and see that integrated/merged into the trunk, 118 | then a pull-request for a piece of new functionality (and integrated into trunk), then perhaps another refactoring (trunk 119 | integrated again). And for those three pull-requests they shared the same Story/card association, but perhaps had different 120 | short-lived branch names - each of which was deleted after merge/integration. 121 | 122 | ## Breaking the contract 123 | 124 | ![](slfb_bad_sharing.png) 125 | 126 | If you merged the part-complete short-lived feature branches to anywhere else, then you have broken the 127 | contract of trunk-based development. For short-lived feature branches, these are **not** allowed: 128 | 129 | 130 | 131 | 1. intermediate merges to main (trunk) - at least where the commit was not able to go live on its own 132 | 2. merges (intermediate or not) to other people's short-lived feature branches 133 | 3. merges (intermediate or not) to any release branches (if you have them) 134 | 4. variations of #2 that are direct from/to the developers clone on their workstation 135 | 5. other developers joining you on your short-lived feature branches - at least who are not your pair-programming partner. 136 | 137 | # Alternatives to short-lived feature branches 138 | 139 | 140 | 141 | There is a more traditional alternative for smaller teams: 142 | [Committing straight to the trunk](/committing-straight-to-the-trunk/). Many people still prefer this way of working, and if the whole team can do it without blowing up the build server, then great job team. 143 | 144 | There is also the patch-review way of working that was outlined in Google's use of Mondrian (2006), and consequentially delivered for non-Google teams with open source tools Gerrit and Rietveld (2007/8). 145 | 146 | These two alternatives, as well as short-lived feature branches are compared in [Styles and Trade-offs](/styles/). 147 | 148 | # References elsewhere 149 | 150 | show references 151 | 152 |
153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 |
26 Apr, 2007, Blog Entry
Introducing Branch By Abstraction
161 |
162 | -------------------------------------------------------------------------------- /content/short-lived-feature-branches/slfb_bad_sharing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/short-lived-feature-branches/slfb_bad_sharing.png -------------------------------------------------------------------------------- /content/short-lived-feature-branches/slfb_pull-push.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/short-lived-feature-branches/slfb_pull-push.png -------------------------------------------------------------------------------- /content/short-lived-feature-branches/slfb_working-copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/short-lived-feature-branches/slfb_working-copy.png -------------------------------------------------------------------------------- /content/strangulation/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-07-08T20:08:11+01:00 3 | title: Application strangulation 4 | weight: 78 5 | --- 6 | 7 | Strangulation is a mechanism by which a very large disruptive change is made in an application or service that, does not 8 | disrupt its ability to go live, even while partially complete. Martin Fowler named this practice (see references below) 9 | after the strangler vines that creep up existing trees, in order to steal sunlight at canopy level of a jungle. 10 | 11 | The trick is to have a mechanism to route invocations of logic between the old and new solutions for the same. Say 12 | you are an Airline, and you had written your first online purchasing experience in Perl. You're now wanting to 13 | do 'Elixir' and its web framework 'Phoenix'. 14 | 15 | Strangulation is where you would use the Apache server that you doubtless had 16 | fronting Perl, to **conditionally** route HTTP requests to Erlang/Elixir/Phoenix. Say your first completed milestone 17 | was 'Loyalty Account View/Edit' you would route based on the URLs the browser was seeking pages for. Obviously 18 | agreeing on URLs (and cookies) is key for the old Perl and new Elixr app. So is deployment in lockstep. 19 | 20 | At some point in the strangulation, you might put Elixir in front Apache/Perl and have traffic drop through to it 21 | instead. That is the residual situation before you delete the last lines of code of Perl and snip that delegation 22 | when the strangulation is complete. 23 | 24 | This relates a little to [Branch by Abstraction](/branch-by-abstraction/). Strangulation is a strategy for 25 | incompatible languages (they are not in the same process), whereas Branch by Abstraction is where the 'from' and 'to' 26 | languages are the same (say Java -> Java), or compatible (Java -> Scala). 27 | 28 | # References elsewhere 29 | 30 | show references 31 | 32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
29 Jun 2004, MartinFowler.com article
Application Strangulation
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
17 Jan 2006, Blog Entry
Great Architects Are Also Stranglers
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
14 Jul 2013, Blog Entry
Legacy Application Strangulation: Case Studies
57 |
58 | -------------------------------------------------------------------------------- /content/styles/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-06-01T20:10:45+01:00 3 | title: Styles and Trade-offs 4 | weight: 60 5 | --- 6 | 7 | 23 | 24 | There are broadly three styles of trunk-based development as a daily developer activity. Depending on the number of 25 | developers in the team, the release cadence, and the desired rate of commits (assuming story-sizes that support that), 26 | you have trade-offs for each of the three: 27 | 28 | ![](/styles/styles-tradeoffs.png) 29 | 30 | ## Committing Straight to the Trunk 31 | 32 | Suitable for active committer counts between 1 and 100. 33 | 34 | {{< figure src="/styles/styles-tradeoffs-l.png" class="floatleft" title="left hand side of the diagram above" >}} 35 | 36 | Traditionally Trunk-Based Development meant committing straight to the shared trunk of the VCS in question. And doing so after 37 | a bunch of steps that together with the commit we will call integration. 38 | 39 | This is a really high throughput way of working, 40 | but it relies on developers being extremely sure their commit is not about the break the build. If they do, then a manual 41 | or automatic revert gets the trunk back to a good state, and you hope nobody did a git-pull or svn-up to bring that bad 42 | commit into their workstation. Or you've engineered it so that does not happen - you publish a known-passing latest commit 43 | number or hash, and make a wrapper for git-pull (svn-up, etc) to go get that instead of HEAD. 44 | 45 | A related challenge is how long "the build" takes to execute by the CI service, versus how frequently the trunk is updated 46 | with commits for all the committers that could. This is important because if the build fails (CI), there could be following 47 | commits that would pass if it were not for the preceding failing commit. To pick that a part could be a challenge if the 48 | commit rate is high enough. Some build-cops lock the trunk at the first sign of a breakage. Best of all is a bot that 49 | reverts specific commits that failed the build, but again that is hard science. If your build is 10 seconds (start to 50 | finish), and there is one commit every five minutes, then you are in a good position. If your build is five minutes and 51 | your team's commits arrive every ten seconds, then you're in hell, and should try something else. 52 | 53 | See [committing straight to the trunk](/committing-straight-to-the-trunk/) for more info. 54 | 55 | ## Short-Lived Feature Branches 56 | 57 | Suitable for active committer counts between 2 and 1000. 58 | 59 | {{< figure src="/styles/styles-tradeoffs-c.png" class="floatleft" title="center of the diagram above" >}} 60 | 61 | Git and Mercurial delivered truly lightweight branching capability. What that meant was that branches could be very quickly 62 | created and receive commits that are momentarily divergent from trunk or main (in our case) and then be merged back later 63 | when ready. Then finally, and crucially, the branch that facilitated that short-lived divergence could be deleted quickly 64 | leaving only the the commits added to the effective lined of commits culminating in HEAD for trunk or main. In that 65 | regard it is identically to the patch review way of working for trunk based development. That was all just a small 66 | data point for Git/Mercurial usage, until GitHub launches and had pull-requests as a feature from launch. Built in to that 67 | was a code review tool. This is a very compelling setup for unsolicited code contributions - making SourceForce and the 68 | Apache Software Foundation appear ancient, by comparison. 69 | 70 | Teams can form around the GitHub pull-request workflow, and still do Trunk-Based Development. What you get (if you've 71 | attached build automation too), is a trunk or main that's never broken (or 1/1000th as likely to). The trade-off is that 72 | you have to persuade co-workers to review your commit(s) in a time frame that suits you. There's a risk that you'd end up 73 | putting more commits in the pull-request than the straight-to-trunk experts would do. But you don't have to. You could 74 | stream four separate facilitating commits all the way into the trunk, and later the fifth that would complete/activate the 75 | feature you were trying to implement as specified by the Agile story/card. Not only is there a risk of more-commits 76 | than you'd do if you could push directly, there's a risk of taking more time too. If your average story size should be 77 | one day, a slow-review and slow-build reality for the pull-request way, might push you into multi-day stories/cards, and 78 | from that be doing the opposite of getting to continuous delivery. 79 | 80 | See [short-lived feature branches](/short-lived-feature-branches/) for more info. 81 | 82 | ## Coupled "Patch Review" System 83 | 84 | Suitable for active committer counts between 2 and 40,000. 85 | 86 | "We do Trunk-Based Development" - Googler Rachel Potvin - @Scale keynote, Sept 2015 (14 mins in): 87 | 88 | ![](/branch-for-release/atscale.png) 89 | 90 | {{< figure src="/styles/styles-tradeoffs-r.png" class="floatright" title="right hand side of the diagram above" >}} 91 | 92 | Perhaps before others in the early 2000's, Google hit a ceiling on how many developers could commit to a trunk in a monorepo, 93 | without tripping each other up. That would be build-breakages mostly, but also commits that wouldn't be up to coding standards 94 | even if the build still passed. Say Google managed to get 1000 developers and QA automators working in with commits straight 95 | into the trunk, before deciding that something needed to gate that. What resulted was a patch review system that would 96 | ultimately be called Mondrian and be announced to the world in 2006 at a tech talk in Mountain View. This was a system that 97 | Google had written to augment Perforce (the VCS they used up to 2012), to provide a place where code could be reviewed per-commit, and 98 | also build-automation results could be collated. 99 | 100 | Today, patch review systems include Gerrit, Rietveld and Phabricator. The latter by Facebook, and the first two with Googler 101 | involvement. These are not branches of course, they are held outside source-control in a relational schema. Their reason 102 | for existence is to marshal pending changes, before they arrive in trunk/main and to guarantee they are good to be 103 | integrated. 104 | 105 | ## The Importance of a Local build 106 | 107 | In all variants of Trunk-Based Development teams run the full build locally (compile, unit tests, a range of integration tests) and see that it pass, **before** declaring 'done' and committing/pushing the work to the eyes of teammates and bots (code review / pull-request), or directly into trunk/main. They do not **at all** use build automation as a crutch in order to determine whether their commit(s) were good or bad. Instead they determine that themselves on their dev workstation or containers/VMs that are dedicated to them, and do so **before** pushing something towards code review and bot scrutiny. As mentioned above keeping this build fast is very important, and not having a fast build is one of the key drivers to other branching models and repo sharding. Indeed it is one of the key drivers to slower release cadences too. 108 | 109 | ## Choosing a style 110 | 111 | While it is best to keep developer teams small, sometime there are business pressures to grow a dev team in order to do 112 | more in parallel. Indeed, with monorepo configurations that could be more dev teams sharing one repo, even if they 113 | have their own release cadences, and separate team organization to other teams. 114 | 115 | If Google had (say) 1000 committers doing "straight to the trunk" for a single monorepo back in 2002, should you? No, not 116 | since alternates are now possible. If Google had the Mondrian of 2006 back in 2002, they would have moved to that way of working sooner. 117 | 118 | What is the cut off point today? Super skilled XP era developers who are in charge of dev teams and can train developers in the applicable 119 | way of working, might say the cut-off is now 100 committers. People who've only ever know the pull-request way of working may 120 | suggest 10 committers is the cut off point. They may even make a case for 2 committers. You could well be in a world 121 | where quorums naturally form within teams, leading to new development directions to be effectively set. In Google, it was 122 | employee #1 Craig Silverstein who initially held Google to the monorepo and trunk-based development. And he perhaps did 123 | that despite quorums forming and group wishes to do something else. 124 | 125 | A list of trade-offs are: 126 | 127 | * Whether your build technology needs to build 'everything' for every commit. Google's Blaze (Bazel in opensource-land) does not. 128 | * Whether your source-control system has a push/pull bottleneck and whether you've reached that with all the committers in one repo 129 | * The median build duration, versus that commit rate. 130 | * How often your build-automation infra falls behind the commits/pull-requests that need to be compiled/tested. 131 | * Whether your developers can avoid using the automated builds as a crutch 132 | * Whether follow-up commits are a workable way of addressing things that need improvements 133 | * Whether the developers are good at separating refactoring commits from functional commits, and indeed "baby commits" generally. 134 | * Whether your team can handle code-review feedback **after** commit/push to trunk/main or not 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /content/styles/styles-tradeoffs-c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/styles/styles-tradeoffs-c.png -------------------------------------------------------------------------------- /content/styles/styles-tradeoffs-l.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/styles/styles-tradeoffs-l.png -------------------------------------------------------------------------------- /content/styles/styles-tradeoffs-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/styles/styles-tradeoffs-r.png -------------------------------------------------------------------------------- /content/styles/styles-tradeoffs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/styles/styles-tradeoffs.png -------------------------------------------------------------------------------- /content/trunk1a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/trunk1a.png -------------------------------------------------------------------------------- /content/trunk1b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/trunk1b.png -------------------------------------------------------------------------------- /content/trunk1c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/trunk1c.png -------------------------------------------------------------------------------- /content/vcs-choices/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-03-02T20:08:11+01:00 3 | title: Version control system choices 4 | weight: 32 5 | --- 6 | 7 | ## Git and Mercurial 8 | 9 | See Git's website{{< ext url="https://git-scm.com" >}} and Mercurial's website{{< ext url="https://www.mercurial-scm.org" >}} 10 | 11 | Git and Mercurial have been popular DVCS technologies for many years. Portals like GitHub make Git, in particular, the default 12 | choice for SCM/VCS/source-control. While the Linux Kernel is maintained with Git, and definitely takes advantage 13 | of the D-Distributed aspect of the DVCS of Git (in that many divergent versions of kernel can exist over 14 | long periods of time), most enterprises are still going to count a single repository as the principal one, and within 15 | that a single branch as the long-term "most valuable" code line. 16 | 17 | It is perfectly possible to do Trunk-Based Development in a Git repository. By convention 'main' is the long term 18 | most valuable branch, and once cloned to your local workstation, the repository gains a nickname of 'origin'. 19 | 20 | ### Forks 21 | 22 | An effective Trunk-Based Development strategy, for Git, depends on the developer maintaining a fork of the origin 23 | (and of 'main' within), and Pull-Requests being the place that ready to merge commits are code reviewed, **before** being 24 | consumed back into `origin:main`. Other branching models use the same Pull-Request process for 25 | code-reviews too - it is the normal way of working with Git since GitHub rolled out the feature. 26 | 27 | ### Size Limits 28 | 29 | Historically, Git and Mercurial were not great at maintaining a zipped history size greater that 1GB. Many 30 | teams have reported that they have a repository size larger than that, so opinions differ. One way that you can reach 31 | that 1GB ceiling quickly is with larger binaries. As Git keeps history in the zipped repository, even a single larger 32 | binary that changes frequently can push the total use above 1GB. 33 | 34 | With the likes of correctly configured Git-LFS extension to Git, though, the 1GB limit can be avoided or delayed 35 | many years. 36 | 37 | Other ways to limit larger clone sizes in git repos include recommending/specifying a partial cloning date horizon 38 | with the "--shallow-since" parameter, or using git's more recent Partial-Clone{{< ext url="https://git-scm.com/docs/partial-clone" >}} features, supported in GitLab for 39 | example, to get the git commit history without getting all the blobs, and instead having the blobs download transparently 40 | on-demand/as-needed. 41 | 42 | Git also has Submodules{{< ext url="https://git-scm.com/docs/git-submodule" >}} and 43 | Subtrees{{< ext url="https://blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree" >}} to allow large 44 | federations of modules, within one cloneable set. For their 45 | Android initiative, Google made Git-repo{{< ext url="http://source.android.com/source/using-repo.html" >}} too. 46 | 47 | 48 | 49 | ### Root level branches 50 | 51 | It'll be clear later why we mention this, but Git and Mercurial maintain branches from the root folder of the 52 | checkout clone, and maintains a single permission for a user in respect of read and/or write on the branch and/or repository. 53 | 54 | ### Future development 55 | 56 | 57 | There is a suggestion that Mercurial is receiving contributions that will allow it to push into the very repository 58 | territory the likes of Google needs. 59 | 60 | Git and Mercurial don't have branch or directory permissions, but some of the platforms that bundle them, add 61 | branch permissions. 62 | 63 | ### Linus Torvalds presenting Git to Googlers 64 | 65 | Back in 2007, Linus Torvalds presented his Bitkeeper inspired Git to Googlers in their Mountain View office: 66 | 67 |
68 | 69 | 70 | 71 | 72 | 73 | He had started making it two years before, and it is now the #1 74 | VCS choice. Google had been running their Monorepo style Trunk for a few years at this point, without regret. Some 75 | Googlers would later extend their Perforce (see below) setup to allow Git operation of local branches on 76 | developer workstations. 77 | 78 | ### Platform Software Choices 79 | 80 | * GitHub {{< ext url="https://github.com" >}} - Git, cloud 81 | * GitHub Enterprise {{< ext url="https://enterprise.github.com/home" >}} - Git in GitHub's on-premises edition 82 | * GitLab {{< ext url="https://about.gitlab.com" >}} - Git, cloud and on-premises install 83 | * Atlassian's Bitbucket server {{< ext url="https://www.atlassian.com/software/bitbucket/server" >}}- Git 84 | * RhodeCode {{< ext url="https://rhodecode.com" >}} - Git, Mercurial and Subversion 85 | * Assembla {{< ext url="https://www.assembla.com" >}} - Git, Mercurial, Perforce and Subversion 86 | * Various Collabnet{{< ext url="http://www.collab.net" >}} products and services for Git 87 | * Microsoft's Visual Studio Team Services{{< ext url="https://visualstudio.microsoft.com/team-services" >}} - Git, cloud 88 | * Microsoft's Team Foundation Server{{< ext url="https://www.visualstudio.com/tfs" >}} - Git in Microsoft's on-premises edition 89 | 90 | ## Perforce 91 | 92 | Perforce's website{{< ext url="https://www.perforce.com" >}} 93 | 94 | ### Vanilla Perforce 95 | 96 | Perforce is a closed-source, industrial strength VCS. Pixar stores everything needed to make a movie in it, and Adidas 97 | store all their designs in it. Until 2012, Google had their Trunk and many tens of terabytes of history in it. 98 | They moved off it to an in-house solution as they outgrew it. Perforce is peculiar in that its 'p4d' (a single server-side 99 | executable binary file) is the whole server and does not need to be installed - just executed. 100 | 101 | Perforce is the last VCS technology that ordinarily maintains the read-only bit on the developer workstation. You 102 | definitely need a plugin for your IDE to handle the wire operations with the server, so you are not confronted with the 103 | fact that source files are read-only. Because the Perforce (p4) client having to involve the server for the flipping of 104 | read-only bits in respect of editing source files, it requires a permanent connection to the server. What that 105 | facilitates is the speed of operation for very large sets of files on the client. The Perforce server already knows what 106 | files need to have updated in your working copy, ahead of you doing 'p4 sync' operation. It negates the need for a 107 | directory traversal looking for locally changed files, and it means the sync operation can be limited to a second or two. 108 | 109 | Historically Perforce was not able to **locally** show the history of the files within it. It needed that server 110 | connection again for history operations. A number of DVCS capabilities in newer versions of Perforce (see below) allow 111 | local history now, though. 112 | 113 | Perforce allows branches to be set up at any sub-directory, not just the root one. It also allows read and/or write 114 | permissions to be specified at any directory (or branch) within large and small source trees. 115 | 116 | #### No Code Review 117 | 118 | Perforce does not have code-review features integrated into its traditional server daemon. By customizing a modified Gitlab 119 | called GitSwarm 'side install', Perforce now has a code review capability. It also has it with an alternate side-install 120 | called just Swarm (a slightly older product), that does not offer the Git capability of GitSwarm, but does add in much 121 | of the team-ware features like code review. 122 | 123 | ### Git Fusion 124 | 125 | There's a VM appliance from the Perforce people, that can sit in your infrastructure and mediate between the vanilla Perforce 126 | server, and your wish to use a pure Git workflow on your development workstation. 127 | 128 | With a Git Fusion clone from a Perforce repository, and client spec was specified, you get the subsetted 129 | representation of the source tree, complete with history. That's a neat feature. Things checked out through Git Fusion 130 | also are not encumbered by the read-only bit feature. 131 | 132 | GitSwarm kinda replaces this. 133 | 134 | ### p4-git and p4-dvcs 135 | 136 | P4-git is very similar to the Git Fusion technology but is not made by the Perforce people themselves. It also does not 137 | require the launching of second server appliance (as Git Fusion does). 138 | 139 | In 2015, the perforce technologies were extended to include custom DVCS features. All the features of p4-git but without 140 | the Git compatibility. 141 | 142 | As for Git Fusion, things checked out through p4-git and p4-dvcs are not encumbered by the read-only bit control of p4d. 143 | 144 | ## Subversion 145 | 146 | Subversion's website{{< ext url="https://subversion.apache.org" >}} 147 | 148 | Subversion (Svn) has been in development for 16 years and was a sorely needed open-source replacement for CVS. It chases some of the 149 | features of Perforce but is developed quite slowly. Nobody has pushed Subversion to the Perforce usage levels, but 150 | that is claimed as a possibility. 151 | 152 | Subversion, like Perforce, has read and write permissions down to the directory and branch. 153 | 154 | Interestingly there is a "Subversion vs Git" website{{< ext url="https://svnvsgit.com/" >}} which attempts to counter 155 | some widely held community beliefs about Subversion and how it stacks up to Git. 156 | 157 | Note also that the Subversion team themselves, do not do Trunk-Based Development, despite Subversion have default root directories 158 | of 'trunk', 'tags' and 'branches' for newly-created repositories. 159 | 160 | ### No Code Review 161 | 162 | Note that Subversion has no local branching capability, and to get code review you need to install third-party servers 163 | along side it. Or, a better choice, use a platform that integrates code review like those below. 164 | 165 | ### Git-Svn 166 | 167 | There is an extension to Git that allows it to deal with a Subversion backend. A Git-subversion clone has all the 168 | local history, local-branching possibilities of Git. The local branching possibilities afforded by this 169 | mode of operation are very handy, and it should work easily with whatever Svn hosting platform you installed. 170 | 171 | Note: That clone from subversion can be many tens of times slower, 172 | than the equivalent clone from Git, because it is recreating the zipped Git history on the client-side as in 173 | uses the classic Subversion wire protocol, which is more chatty. Indeed the initial clone of years of commits for a 174 | reasonably sized team can take many hours. 175 | 176 | ### Platform Software Choices 177 | 178 | * RhodeCode{{< ext url="https://rhodecode.com" >}} - installable on-premises 179 | * Various Collabnet{{< ext url="http://www.collab.net" >}} products and services. 180 | * ProjectLocker{{< ext url="http://projectlocker.com" >}} - cloud 181 | * Deveo{{< ext url="https://deveo.com/svn-hosting" >}} - cloud 182 | * RiouxSvn{{< ext url="https://riouxsvn.com" >}} - cloud 183 | * SilkSvn{{< ext url="https://sliksvn.com" >}} - cloud 184 | * Assembla{{< ext url="https://www.assembla.com/subversion" >}} - cloud and installable on-premises 185 | * XP-dev{{< ext url="https://xp-dev.com" >}} - cloud 186 | * Codeplex{{< ext url="https://www.codeplex.com" >}} - cloud 187 | 188 | ## Team Foundation Server - TFS 189 | 190 | TFS's website{{< ext url="https://www.visualstudio.com/tfs" >}} 191 | 192 | Microsoft launched TFS in the mid-2000's with a **custom VCS technology** "TFVC". It is said that they have an internal 193 | 'SourceDepot' tool that is a special version of Perforce compiled for them in the nineties, and that TFS reflects some 194 | of the ways of working of that technology. It has grown to be a multifaceted server platform. Perhaps even a 195 | one-stop shop for the whole enterprise's needs for application lifecycle management. More recently with TFS, 196 | Microsoft encouraged a use of Git within it, rather than then proprietary VCS they initially developed. 197 | 198 | TFS is perfectly compatible with a Trunk-Based Development usage. 199 | 200 | Note: Microsoft are donating back to the Git community with a Git Virtual File System, which allows some Monorepo 201 | usage for Git{{< ext url="https://github.com/Microsoft/VFSForGit" >}}. 202 | 203 | ## PlasticSCM 204 | 205 | Plastic's website{{< ext url="https://www.plasticscm.com" >}} 206 | 207 | PlasticSCM is a modern DVCS like Git and Mercurial, but closed-source. It is compatible with Trunk-Based Development and quite 208 | self-contained (has integrated code review, etc). Plastic is very good with bigger binaries and comes with an 209 | intuitive "Branch Explorer" to see the evolution of branches, view diffs, execute merges, etc. For sizes of individual 210 | repositories, multiple terabytes is not unheard of. A least for some of the games-industry customers. 211 | 212 | It is also the first modern VCS to have semantic merge - it understands 213 | select programming languages and the refactorings developers perform on them. For example "move method", where that 214 | method is 50 lines long, is not 50 lines added and 50 deleted in one commit, it is a much more *exact* and terse diff 215 | representation. 216 | 217 | Plastic even calmly handles a situation where one developer moves a method within a source, and another simultaneously 218 | changes the contents of the method in its former location. Plastic does not consider that a clash at all, and just does 219 | the merge quietly - the method moves and is changed in its new location. 220 | 221 | -------------------------------------------------------------------------------- /content/vcs-choices/linus-git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/content/vcs-choices/linus-git.png -------------------------------------------------------------------------------- /content/vcs-features/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-03-01T20:08:11+01:00 3 | title: Version control system features 4 | weight: 31 5 | --- 6 | 7 | **Desirable VCS features, that is** 8 | 9 | To great degree, Trunk-Based Development is possible on any Version Control System (VCS) that does atomic commits (spoiler: all do 10 | that came after CVS). Productivity and governance are what divides them, though. 11 | 12 | ## Productivity 13 | 14 | It turns out there are many 15 | productivity related reasons that teams quit one technology and go to another. It does not matter whether the tool is a commercial 16 | or an open source one. At least to its end users it does not. 17 | 18 | By speed we mean two things primarily: 19 | 20 | 1. The speed at which we can pull/update/sync changes out of a remote server repository 21 | 2. The speed at which we can commit/push changes back to that remote server 22 | 23 | And three other secondary things that support little and often: 24 | 25 | 1. Advanced Merging 26 | 2. Code Review 27 | 3. Continuous Integration 28 | 29 | ### Pull/update/sync speed 30 | 31 | If you have at least once done a checkout of the source of the project, any subsequent pull/update/sync is going to bring down 32 | differences from the previous checkout or update. If you do two updates back to back quickly, the second one is likely to bring 33 | down nothing. The time taken for the version control tool to determine that nothing is due from the remote main 34 | repository is the biggest clue as to how intrinsically fast it is. 35 | 36 | Technologies that only keep head revision on the checkout, ordinarily have to walk the entire directory structure 37 | looking for changed files and do handshaking to the server for each one. That is definitely Subversion and its predecessor CVS. 38 | Batching of those exchanges speeds it up, but there is still a slowdown related to the breadth and depth of the source 39 | tree. Perforce makes the operation faster because the server-side is poised for the sync operation at all times, by 40 | keeping your tree and which revision you have for each file in RAM. It does this at the cost of maintaining read-only 41 | bits for files (be sure and use an IDE that silently handles the Perforce interactions). Perforce can effectively be much 42 | faster for this back-to-back pull/update/sync test of speed because it kinda already knows the answer to the question. 43 | 44 | CVS, Subversion and Perforce offer you the choice of checking out a subdirectory. In a monorepo situation, you would 45 | consider that a nice feature. At least if you've recursively laid out services and applications within the trunk. 46 | 47 | Git and Mercurial have a single point of checkout (Git's 'clone' operation) for the whole repository. There are no sub-directory checkouts 48 | for these two. All commits since that 49 | last 'pull' will be pulled down. This happens before the directory walk to determine what has changed locally. As such, that 50 | pull operation if very fast - there's no chit chat over the wire things, and the stuff on the server-side was already 51 | zipped and pretty much ready for transfer. This will be the case even for situations where a particular 52 | file has been changed a dozen times since you last pulled it down via a sync operation. You might think 53 | this is costly, but in practice, Git is incredibly fast. 54 | 55 | ### Commit/push speed 56 | 57 | Things are more equal here between the tools we highlight as viable. 58 | 59 | Subversion and Perforce send up deltas of changed files to the server. Some directory walking can slow this down. There 60 | is inevitably a lot of chit-chat on the wire for these operations. 61 | 62 | Git and Mercurial do the same, but before you push to the remote repo it will make you commit locally which is incredibly 63 | fast. Before you push to the shared remote repository, these two will make you pull first. 64 | 65 | Perforce and Subversion will allow you to commit/push changes to the remote, without necessarily having the latest versions (and 66 | incidentally all the intervening ones) locally first. It will only allow that if there was no clash on the lines changed. 67 | 68 | Perforce can cheat again, but taking advantage of the read-only bit, and therefore already knows which files definitely 69 | have **not** changed between the remote main repo and local working copy. It still feels slower than it should be, though. 70 | 71 | ### Three-way merge tools 72 | 73 | Developers (hopefully in pairs) are going to have to become skilled in arbitrating over merges for the commits they 74 | are trying to promote to the remote trunk, as well as the changes they are updating from that it in the case that they 75 | have work in progress in their working-copy. Trunk-Based Development teams, you see, are merging more often. Albeit 76 | those are smaller merges, and they are implicitly merged to your working copy. 77 | 78 | Perforce's three-way merge tool (P4Merge) is good enough on its own to be attractive to teams using other VCS 79 | technologies. For those other technologies, P4Merge is just a config setting away from being usable. 80 | 81 | Semantic merge is the next step up in the science of source-control. See [Plastic SCM](/vcs-choices/index.html#plasticscm). 82 | 83 | ### Code Review 84 | 85 | Integrated code review turned out to to be the killer feature of VCS tools. This should have been clear from the moment Mondrian 86 | was unveiled by Guido van Rossum (Mr. Python) in a [publicized Google 'tech talk' in 2006](https://www.youtube.com/watch?v=sMql3Di4Kgc). It delivered pre-commit 87 | code reviews to developers and gamified the activity of code review to some degree. Google was 88 | using Perforce back then (they changed to an in-house technology in 2012), and it did not have code review built in, so 89 | they had to make Mondrian (which was the final form of years of intermediate deliverables for the same). Thus 90 | Mondrian being created tightly coupled code review to the hourly activities of developers on the trunk. 91 | 92 | GitHub (not Git) was next for the non-Google dev world with a built-in code-review tool (and workflow). Again this was 93 | effectively pre-commit - or at least commit to the main. 94 | 95 | There were (and are) other technologies for code review such as Crucible (Atlassian), UpSource (JetBrains), Gerrit, 96 | Phabricator, but integration into a platform experience is key. GitLab and RhodeCode are emerging platforms. 97 | 98 | Read more in [Game Changers - Google's Mondrian](/game-changers/#googles-internal-devops-2006-onwards) and 99 | [Game Changers - GitHub's Pull Requests](/game-changers/#githubs-entire-initial-platform-2008). 100 | 101 | ### Continuous Integration testing 102 | 103 | This goes hand in hand with the Code Review capability. CI Servers kick in for commits are pushed up code review branches, 104 | or at least against change sets that are entering code review and validate them. The regular build, and maybe some 105 | additional steps are executed against them and the results of those made available to code reviewers, Facebook has a 106 | Service Level Agreement to have those complete ten minutes after the commit has been placed in the 'needs code review' 107 | queue. 108 | 109 | The reality of VCS platforms today is that they only provided hooks (web-hooks most likely) into other CI servers. The 110 | best combinations of VCS platform and CI server do so for any of the branches in play, including code-review branches 111 | (forks). 112 | 113 | ## Governance 114 | 115 | It turns out that enterprises like the ability to carve up permissions, and set read and write permissions throughout 116 | their repositories. 117 | 118 | ### fine grained permissions 119 | 120 | All VCS usages need users accounts to be able to accept changes back, even if they don't all require accounts in order 121 | to be able to read. In the enterprise user accounts will be required to be able to read too. 122 | 123 | The larger the enterprise the more likely it will be that they have permissions carved up in more fine grained ways. 124 | For example "can commit to the trunk, but cannot create release branches" will be common for Trunk-Based Development 125 | teams that make release branches. Not all VCS technologies support that. Git, for example, does not, but some of the 126 | portal experiences around it, add the feature. In reality, though, you cannot stop people from making branches in a DVCS, 127 | but you can prevent them from pushing them to the shared repository. 128 | 129 | Deeper still, some enterprises will want to set permissions down the directory level within a branch within a repository. 130 | This could be as simple as a gate, to ensure that process is adhered to around checkins, but could also be to guard 131 | some secrets in a Monorepo implementation. That last is counter to some of the "common code ownership" ideals of 132 | modern software engineering though. 133 | 134 | ### Size / Scale 135 | 136 | Many teams, particularly those with Monorepo configurations or large binary files (like Games companies), want to have 137 | limitless server-side storage for their repository. Git and Mercurial are inching towards bigger and bigger 138 | capacity, but there are still a few snafus to work through - how to cleanly/safely reduce the size of the client side clone 139 | history (while still being able to push changes back to the server) is a challenge. 140 | 141 | Microsoft released Git Virtual File System (~~GitVFS~~ ~~GVFS~~ VFS for Git{{< ext url="https://github.com/Microsoft/VFSForGit" >}} - Windows only) in early 2017, to layer on some of the things they had 142 | become used to in the in-house recompilation of Perforce (SourceDepot - used from 1998 to 201x), as well as native VCS 143 | of their commercial offering Team Foundation Server (TFS). 144 | -------------------------------------------------------------------------------- /content/youre-doing-it-wrong/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-09-01T20:08:11+01:00 3 | title: You're doing it wrong 4 | weight: 91 5 | --- 6 | 7 | ## Merely naming a branch trunk. 8 | 9 | Say you are using Subversion, and you accepted its default directory design, when you made a new repository. That will 10 | give you 'trunk', 'tags' and 'branches' as directory names. The mere fact that you have a branch called trunk does not 11 | mean you are doing Trunk-Based Development. "We merge branches back to trunk often" can be heard a lot in the industry, 12 | and if you are grouping multiple developers on those branches or they are not deleted after a couple of days, then it is 13 | not the Trunk-Based Development branching model. 14 | 15 | ## Cherry-pick of bug fixes from release branches to the trunk 16 | 17 | All your developers are using a trunk and they are doing the right thing with respect to not breaking the build. Your release 18 | cadence is infrequent enough to allow you to cut a release branch on a just in time basis and then harden that in the run 19 | up to the actual release. 20 | 21 | But, if you are fixing bugs on the release branch and merging them down to the trunk you are doing 22 | it wrong. There is a chance you might forget to merge it down, and then there is going to be a regression at the next 23 | release moment (fresh branch cut from the trunk). Then egg on face, and recriminations. 24 | 25 | Bugs should be reproduced and fixed on the trunk, and then **cherry-picked** to the release branch. A build should 26 | happen from that, a second confirmation that the issue has been remediated, and that deployment should go live (perhaps 27 | a point release). If you can not reproduce bugs on the trunk (truly rare), then you have permission to go ahead and 28 | reproduce it on the release branch, fix it there, and merge back. 29 | 30 | ## Merging rather than cherry-pick to/from a release branch 31 | 32 | The developers cut a release branch because their release cadence is low, and they're hardening and certifying the release 33 | there. BUT in the days that lead up to the release, they are also doing general merges up to the release branch from 34 | the trunk. That is not right - it seems like they cut the branch on the wrong day. Maybe the business people on the team 35 | are pushing too hard to make a date. 36 | 37 | Cherry-picking every commit since the branch-cut to the branch from the trunk is the same thing of course. 38 | 39 | ## Duration of 'short-lived' feature branches 40 | 41 | The [short-lived feature branch](/short-lived-feature-branches/) should only last a day or two and never diverge from the trunk enough so that a 42 | merge back is problematic. After the seal of approval from code reviewers and CI 43 | daemons, it should be merged back into the trunk. It should be deleted, as proof of convergence. 44 | The developer in question may then go ahead and make the next short-lived feature branch for the next story/task they're doing. 45 | 46 | ## Numbers of developers on 'short-lived' feature branches 47 | 48 | If there is more that one developer (and the developer's pairing partner) on the same [short-lived feature branch](/short-lived-feature-branches/), 49 | then that branch is at risk of not being short-lived. It is at risk of being more and more like a release branch 50 | under active development, and not short at all. 51 | 52 | There is a risk too, that a developer may choose to pull changes to their workstation **from a short-lived feature 53 | branch** rather from the trunk. They may think that the code review for that short-lived feature branch is going to take 54 | too long, or they need the changes before they are ready. Unfortunately, there is no way that the current generation of 55 | code portals can prevent people pulling changes from non-trunk branches. 56 | 57 | ## Every day not being the same for developers. 58 | 59 | Developers take stories or tasks from the backlog, implement them with tests, and push them through code review and quality checks 60 | into the shared trunk. They don't slow down that activity as they get closer to a release date. At least the majority do not. A very 61 | small subset of the development team may focus on the release candidates being made from the release branch (if that 62 | branch exists at all), and towards fixes in the trunk that will be cherry-picked into that branch. For most of the team, though, 63 | every day looks the same. Also proximity to a release does not slow down the rate at which changes are being pushed into the 64 | trunk. **There is certainly no "code freeze" with Trunk-Based Development**, as team leadership focuses on protecting the 65 | majority of developers from the distraction of release preparation. If that protection is missing, something needs 66 | fine-tuning, and the branching model could be it. 67 | 68 | ## Keeping a single release branch 69 | 70 | If you are making a release branch, you should not keep it alive for a series of major releases. You principal 71 | mechanism to land code on that branch is the branch creation itself. After that only cherry picks for bug 72 | fixes as found. Even then they should diminish over time. If release branches is your model then the reality should 73 | be that pressure mounts to create a new release branch (from trunk), and evidence is that there is no more 74 | cherry-picks from trunk to the old release branch. In terms of release you might have witnessed releases 1.1, 1.1.1 and 1.1.2 be done from tags on that release branch, but the cherry picks should diminish to zero over time, and activity reconvene around an newer release branch - for the 1.2 release probably. 75 | 76 | ## Merge from one release branch to another release branch 77 | 78 | We only merge from trunk to release branches (if we branch at all), and then only a small percentage of the 79 | commits to trunk, and only using the cherry-pick method of merging. Sure, two release branches could be in 80 | play for short periods of time, but the team should cherry-pick merge from trunk to two different branches. 81 | In some VCS tools (Perforce and Subversion) those can be in the same atomic commit, but that is not 82 | strictly necessary. 83 | 84 | ## Merge everything back from a release branch at the end of the release branch 85 | 86 | Bug fixing on the release branch? If yes, then that is covered above. If you are not doing that then you have no need 87 | to merge anything back from the release branch - you can just delete it after you're sure there will not be any more 88 | releases from it. 89 | -------------------------------------------------------------------------------- /fix_html.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from BeautifulSoup import BeautifulSoup 3 | 4 | if sys.argv[2] == "true": 5 | rel = "../" 6 | else: 7 | rel = "" 8 | soup = BeautifulSoup(open(sys.argv[1]).read()) 9 | for a in soup.findAll('a'): 10 | if a.get('href').endswith('/'): 11 | a['href'] = a['href'] + 'index.html' 12 | if a.get('href').startswith('/'): 13 | a['href'] = rel + a['href'][1:] 14 | 15 | for img in soup.findAll('img'): 16 | if img.get('src').startswith('/'): 17 | img['src'] = rel + img['src'][1:] 18 | 19 | content = str(soup).replace("ZZ/", rel).replace("url(/ima", "url(" + rel + "ima") 20 | open(sys.argv[1], 'wb').write(content) -------------------------------------------------------------------------------- /footer_refs.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup 2 | import sys 3 | import json 4 | 5 | soup = BeautifulSoup(open(sys.argv[1]).read(), "html.parser") 6 | 7 | # Replace ref anchors to chapter footer references 8 | 9 | ftr = 0 10 | refs = "

Web references inline in this chapter

" 11 | for span in soup.find_all("span", { "class" : "rref" }): 12 | ftr += 1; 13 | try: 14 | refs += str(ftr) + ": " + span.a['href'] + "
" 15 | except TypeError: 16 | print str(span) 17 | raise 18 | new_tag = soup.new_tag('sup') 19 | new_tag.string = "[" + str(ftr) + "]" 20 | span.a.replace_with(new_tag) 21 | refs += "

" 22 | 23 | # Write a inline links section if needed 24 | 25 | if ftr > 0: 26 | h2 = soup.find("h2", { "id" : "references-elsewhere" }) 27 | if h2 is not None: 28 | ix = h2.parent.contents.index(h2) 29 | h2.parent.insert(ix, BeautifulSoup(refs, "html.parser")) 30 | else: 31 | soup.body.article.div.insert(len(soup.body.article.div.contents), BeautifulSoup(refs, "html.parser")) 32 | 33 | # Replace anchors to other TBD pages with chapter references. 34 | 35 | for a in soup.find_all("a"): 36 | href = a['href'].replace('../','') 37 | if ":" not in href: 38 | if "#" not in href: 39 | sub_chapters = json.loads(open(href.replace(".html", ".json")).read()) 40 | a.replace_with(BeautifulSoup(""+a.text+"[ch: "+sub_chapters['ch']+"]", "html.parser")) 41 | else: 42 | file = href.replace(".html", ".json") 43 | file = file[0:file.index("#")] 44 | subchap = href[href.index("#")+1:] 45 | sub_chapters = json.loads(open(file).read()) 46 | try: 47 | a.replace_with(BeautifulSoup(""+a.text+"[ch: " + sub_chapters['h2s'][subchap] + "]", "html.parser")) 48 | except KeyError: 49 | print str(a) + " -->" + subchap + "<" 50 | raise 51 | 52 | open(sys.argv[1], 'wb').write(str(soup)) 53 | 54 | # All remaining anchors ... requires a reload of soup for some reason. 55 | 56 | soup = BeautifulSoup(open(sys.argv[1]).read(), "html.parser") 57 | for a in soup.find_all("a"): 58 | a.replace_with(BeautifulSoup(""+a.text+" ["+a['href']+"]", "html.parser")) 59 | 60 | open(sys.argv[1], 'wb').write(str(soup)) 61 | 62 | # Reload soup again for some reason. 63 | 64 | soup = BeautifulSoup(open(sys.argv[1]).read(), "html.parser") 65 | sub_chapters = json.loads(open(sys.argv[1].replace(".html", ".json")).read()) 66 | h2_ix = 0 67 | for h2 in soup.findAll('h2'): 68 | if 'references-elsewhere' not in str(h2) and 'Books promoting' not in str(h2) and 'Reports promoting' not in str(h2): 69 | h2_ix += 1 70 | h2.string = sub_chapters['ch'] + "." + str(h2_ix) + ": " + h2.text 71 | 72 | open(sys.argv[1], 'wb').write(str(soup)) -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/static/.gitkeep -------------------------------------------------------------------------------- /static/google8536929dca466e24.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google8536929dca466e24.html -------------------------------------------------------------------------------- /themes/hugo-material-docs/archetypes/default.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- -------------------------------------------------------------------------------- /themes/hugo-material-docs/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/themes/hugo-material-docs/images/screenshot.png -------------------------------------------------------------------------------- /themes/hugo-material-docs/images/tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/themes/hugo-material-docs/images/tn.png -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/404.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/themes/hugo-material-docs/layouts/404.html -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/_default/__list.html: -------------------------------------------------------------------------------- 1 | {{ partial "head" . }} 2 | 3 |
4 |
5 |
6 | 7 | 8 | 9 | 10 | 11 |
12 | {{ partial "header" . }} 13 |
14 | 15 |
16 |
17 | {{ partial "drawer" . }} 18 |
19 | 20 |
21 |
22 |

Pages in {{ .Title | singularize }}

23 | 24 | {{ range .Data.Pages }} 25 | 26 |

{{ .Title }}

27 |
28 | 29 |
30 | {{ printf "%s" .Summary | markdownify }} 31 | 32 |
33 | {{ end }} 34 | 35 | 44 |
45 |
46 | 47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | 57 | {{ partial "footer_js" . }} 58 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/_default/single.html: -------------------------------------------------------------------------------- 1 | {{ partial "head" . }} 2 | 3 | {{ if (eq (trim .Site.Params.provider " " | lower) "github") | and (isset .Site.Params "repo_url") }} 4 | {{ $repo_id := replace .Site.Params.repo_url "https://github.com/" ""}} 5 | {{ .Scratch.Set "repo_id" $repo_id }} 6 | {{ end }} 7 | 8 |
9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 |
17 | {{ partial "header" . }} 18 |
19 | 20 |
21 |
22 | {{ partial "drawer" . }} 23 |
24 | 25 |
26 |
27 |

{{ .Title }} {{ if .IsDraft }} (Draft){{ end }}

28 | 29 | {{ .Content }} 30 | 31 | 41 | 42 |
43 | {{ partial "footer" . }} 44 |
45 |
46 |
47 | 48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | 58 | {{ partial "footer_js" . }} 59 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/index.html: -------------------------------------------------------------------------------- 1 | {{ partial "head" . }} 2 | 3 | {{ if (eq (trim .Site.Params.provider " " | lower) "github") | and (isset .Site.Params "repo_url") }} 4 | {{ $repo_id := replace .Site.Params.repo_url "https://github.com/" ""}} 5 | {{ .Scratch.Set "repo_id" $repo_id }} 6 | {{ end }} 7 | 8 |
9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 |
17 | {{ partial "header" . }} 18 |
19 | 20 |
21 |
22 | {{ partial "drawer" . }} 23 |
24 | 25 |
26 |
27 | {{ range where .Site.Pages "Type" "index" }} 28 |

{{ .Title }} {{ if .IsDraft }} (Draft){{ end }}

29 | 30 | {{ .Content }} 31 | {{ end }} 32 | 33 | 42 | 43 |
44 | {{ partial "footer" . }} 45 |
46 |
47 |
48 | 49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | 59 | {{ partial "footer_js" . }} 60 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/partials/drawer.html: -------------------------------------------------------------------------------- 1 | 51 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/partials/footer.html: -------------------------------------------------------------------------------- 1 | {{ $pages := .CurrentSection.Pages.ByWeight.Reverse }} 2 | {{ if .IsPage }} 3 | 63 | {{ end }} 64 | 65 | {{ if .IsHome }} 66 | {{ if gt (len .Site.Pages) 2 }} 67 | 91 | {{ end }} 92 | {{ end }} 93 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/partials/footer_js.html: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | {{ range .Site.Params.custom_js }} 13 | 14 | {{ end }} 15 | 16 | 81 | 82 | {{ with .Site.GoogleAnalytics }} 83 | 114 | {{ end }} 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/partials/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {{ .Title }}{{ if not .IsHome }} - {{ .Site.Title }}{{ end }} 13 | {{ .Hugo.Generator }} 14 | 15 | {{ with .Site.Params.description }} 16 | 17 | {{ end }} 18 | 19 | {{ with .Site.Params.author }} 20 | 21 | {{ end }} 22 | 23 | 24 | {{ with .Site.Title }}{{ end }} 25 | {{ with .Site.Params.logo }}{{ end }} 26 | {{ with .Site.Title }}{{ end }} 27 | 28 | 29 | 30 | 31 | 32 | 33 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | {{/* set default values if no custom ones are defined */}} 56 | {{ $text := or .Site.Params.font.text "Roboto" }} 57 | {{ $code := or .Site.Params.font.code "Roboto Mono" }} 58 | 59 | 67 | 68 | {{ range .Site.Params.custom_css }} 69 | 70 | {{ end }} 71 | 72 | 73 | {{ with .RSSLink }} 74 | 75 | 76 | {{ end }} 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/partials/header.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/partials/nav.html: -------------------------------------------------------------------------------- 1 | {{ $currentNode := . }} 2 | 3 | {{ range .Site.Menus.main.ByWeight }} 4 | 5 | {{ $.Scratch.Set "currentMenuEntry" . }} 6 |
  • 7 | {{ if .HasChildren }} 8 | {{ .Name | title }} 9 | 15 | {{ else }} 16 | {{ partial "nav_link" $currentNode }} 17 | {{ end }} 18 |
  • 19 | {{ end }} 20 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/partials/nav_link.html: -------------------------------------------------------------------------------- 1 | {{ $currentMenuEntry := .Scratch.Get "currentMenuEntry" }} 2 | {{ $isCurrent := eq .Permalink ($currentMenuEntry.URL | absURL | printf "%s") }} 3 | 4 | 5 | 6 | {{ $currentMenuEntry.Pre }} 7 | {{ $currentMenuEntry.Name }} 8 | 9 | 10 | {{ if $isCurrent }} 11 | 13 | {{ end }} 14 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/shortcodes/ext.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/shortcodes/note.html: -------------------------------------------------------------------------------- 1 |
    2 |

    {{ .Get "title" }}

    3 | {{ printf "%s" .Inner | markdownify }} 4 |
    -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/shortcodes/quote.html: -------------------------------------------------------------------------------- 1 |
    2 |

    {{ .Get "title" }}

    3 | {{ printf "%s" .Inner | markdownify }} 4 |
    5 |
    -------------------------------------------------------------------------------- /themes/hugo-material-docs/layouts/shortcodes/warning.html: -------------------------------------------------------------------------------- 1 |
    2 |

    {{ .Get "title" }}

    3 | {{ printf "%s" .Inner | markdownify }} 4 |
    5 |
    -------------------------------------------------------------------------------- /themes/hugo-material-docs/static/fonts/icon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/themes/hugo-material-docs/static/fonts/icon.eot -------------------------------------------------------------------------------- /themes/hugo-material-docs/static/fonts/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/static/fonts/icon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/themes/hugo-material-docs/static/fonts/icon.ttf -------------------------------------------------------------------------------- /themes/hugo-material-docs/static/fonts/icon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/themes/hugo-material-docs/static/fonts/icon.woff -------------------------------------------------------------------------------- /themes/hugo-material-docs/static/google8536929dca466e24.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google8536929dca466e24.html -------------------------------------------------------------------------------- /themes/hugo-material-docs/static/images/LogoSlim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/themes/hugo-material-docs/static/images/LogoSlim.png -------------------------------------------------------------------------------- /themes/hugo-material-docs/static/images/ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/themes/hugo-material-docs/static/images/ext.png -------------------------------------------------------------------------------- /themes/hugo-material-docs/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/themes/hugo-material-docs/static/images/favicon.ico -------------------------------------------------------------------------------- /themes/hugo-material-docs/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-hammant/tbd/9ca39292ab0760e4aabb873a93526db40825c1d7/themes/hugo-material-docs/static/images/logo.png -------------------------------------------------------------------------------- /themes/hugo-material-docs/static/stylesheets/highlight/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | * overwrite the current primary color of the 3 | * theme that is used as fallback in codeblocks 4 | */ 5 | .article pre code { 6 | color: rgba(0, 0, 0, 0.8) !important; 7 | } 8 | 9 | 10 | /* 11 | HIGHLIGHT.JS THEME 12 | 13 | tweaked version of the Github theme 14 | */ 15 | 16 | .hljs { 17 | display:block; 18 | overflow-x:auto; 19 | } 20 | 21 | .hljs-comment, 22 | .hljs-quote { 23 | color:#998; 24 | font-style:italic; 25 | } 26 | 27 | .hljs-keyword, 28 | .hljs-selector-tag, 29 | .hljs-subst { 30 | color:#333; 31 | font-weight:700; 32 | } 33 | 34 | .hljs-number, 35 | .hljs-literal, 36 | .hljs-variable, 37 | .hljs-template-variable, 38 | .hljs-tag .hljs-attr { 39 | color:teal; 40 | } 41 | 42 | .hljs-string, 43 | .hljs-doctag { 44 | color:#d14; 45 | } 46 | 47 | .hljs-title, 48 | .hljs-section, 49 | .hljs-selector-id { 50 | color:#900; 51 | font-weight:700; 52 | } 53 | 54 | .hljs-subst { 55 | font-weight:400; 56 | } 57 | 58 | .hljs-type, 59 | .hljs-class .hljs-title { 60 | color:#458; 61 | font-weight:700; 62 | } 63 | 64 | .hljs-tag, 65 | .hljs-name, 66 | .hljs-attribute { 67 | color:navy; 68 | font-weight:400; 69 | } 70 | 71 | .hljs-regexp, 72 | .hljs-link { 73 | color:#009926; 74 | } 75 | 76 | .hljs-symbol, 77 | .hljs-bullet { 78 | color:#990073; 79 | } 80 | 81 | .hljs-built_in, 82 | .hljs-builtin-name { 83 | color:#0086b3; 84 | } 85 | 86 | .hljs-meta { 87 | color:#999; 88 | font-weight:700; 89 | } 90 | 91 | .hljs-deletion { 92 | background:#fdd; 93 | } 94 | 95 | .hljs-addition { 96 | background:#dfd; 97 | } 98 | 99 | .hljs-emphasis { 100 | font-style:italic; 101 | } 102 | 103 | .hljs-strong { 104 | font-weight:700; 105 | } 106 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/static/stylesheets/temporary.css: -------------------------------------------------------------------------------- 1 | /* This file only exists (temporarily) until the 2 | custom styling can be replaced with the 3 | implementation of the upstream project. 4 | */ 5 | 6 | blockquote { 7 | padding: 0 20px; 8 | margin: 0 0 20px; 9 | font-size: inherit; 10 | border-left: 5px solid #eee; 11 | } 12 | -------------------------------------------------------------------------------- /themes/hugo-material-docs/theme.toml: -------------------------------------------------------------------------------- 1 | name = "Material Docs" 2 | license = "MIT" 3 | licenselink = "https://github.com/digitalcraftsman/hugo-material-docs/blob/master/LICENSE.md" 4 | description = "A material design theme for documentations." 5 | homepage = "https://github.com/digitalcraftsman/hugo-material-docs" 6 | tags = ["material", "documentation", "docs", "google analytics", "responsive"] 7 | features = ["", ""] 8 | min_version = 0.15 9 | 10 | [author] 11 | name = "Digitalcraftsman" 12 | homepage = "https://github.com/digitalcraftsman" 13 | 14 | # If porting an existing theme 15 | [original] 16 | name = "Martin Donath" 17 | homepage = "http://struct.cc/" 18 | repo = "https://github.com/squidfunk/mkdocs-material" 19 | -------------------------------------------------------------------------------- /toc_number_applier.py: -------------------------------------------------------------------------------- 1 | from BeautifulSoup import BeautifulSoup 2 | import json 3 | import sys 4 | 5 | toc2 = json.loads(open("toc2.json").read()) 6 | soup = BeautifulSoup(open(sys.argv[1]).read()) 7 | ch = 0 8 | 9 | ch_breakdown = {} 10 | ch_breakdown['h2s'] = {} 11 | 12 | for h1 in soup.findAll('h1'): 13 | for key, ix in toc2.items(): 14 | if h1.text in key: 15 | span = h1.find('span') 16 | if span is not None: 17 | span.string = key 18 | ch = ix; 19 | 20 | h2_ix = 0 21 | for h2 in soup.findAll('h2'): 22 | if 'references-elsewhere' not in h2['id']: 23 | h2_ix += 1 24 | ch_breakdown['h2s'][h2['id']] = str(ch) + "." + str(h2_ix) 25 | ch_breakdown["ch"] = str(ch) 26 | 27 | for footer in soup.findAll('footer'): 28 | footer.replaceWith('') 29 | for aside in soup.findAll('aside'): 30 | aside.replaceWith('') 31 | 32 | open(sys.argv[1].replace(".html",".json"), 'wb').write(json.dumps(ch_breakdown)) 33 | open(sys.argv[1], 'wb').write(str(soup)) -------------------------------------------------------------------------------- /toc_numberer.py: -------------------------------------------------------------------------------- 1 | from BeautifulSoup import BeautifulSoup 2 | import json 3 | 4 | soup = BeautifulSoup(open("toc.html").read()) 5 | ch = 0 6 | m = {} 7 | m2 = {} 8 | for a in soup.findAll('a'): 9 | if 'index.html' in a.get('href'): 10 | ch += 1 11 | a.string = str(ch) + ". " + a.text 12 | m[a.get('href').replace("/index.html", "")] = ch 13 | m2[a.text] = ch 14 | 15 | open("toc.json", 'wb').write(json.dumps(m)) 16 | open("toc2.json", 'wb').write(json.dumps(m2)) 17 | 18 | open("toc.html", 'wb').write(str(soup)) --------------------------------------------------------------------------------