Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: app/views/doc/versioning.html

Issue 162403002: Remove docs and point to ones on dartlang.org. (Closed) Base URL: https://github.com/dart-lang/pub-dartlang.git@master
Patch Set: Fit in 80 columns. Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « app/views/doc/pubspec.html ('k') | app/views/index.mustache » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 <ol class="toc">
2 <li><a href="#a-name-and-a-number">A name and a number</a></li>
3 <li><a href="#shared-dependencies-and-unshared-libraries">Shared dependencies and unshared libraries</a></li>
4 <li><a href="#version-lock">Version lock</a></li>
5 <li><a href="#version-constraints">Version constraints</a></li>
6 <li><a href="#semantic-versions">Semantic versions</a></li>
7 <li><a href="#constraint-solving">Constraint solving</a></li>
8 <li><a href="#constraint-context">Constraint context</a></li>
9 <li><a href="#lockfiles">Lockfiles</a></li>
10 <li><a href="#when-things-go-wrong">When things go wrong</a></li>
11 <li><a href="#summary">Summary</a></li>
12 </ol>
13
14 <p>One of pub&rsquo;s main jobs is helping you work with versioning. Here, I&rsq uo;ll
15 explain a bit about the history of versioning and pub&rsquo;s approach to it.
16 Consider this to be advanced information. If you want a better picture of <em>wh y</em>
17 pub was designed the way it was, read on. If you just want to <em>use</em> pub, the
18 <a href="index.html">other docs</a> will serve you better.</p>
19
20 <p>Modern software development, especially web development, leans heavily on
21 reusing lots and lots of existing code. That includes code <em>you</em> wrote in the
22 past, but also stuff from third-parties, everything from big frameworks to tiny
23 little utility libraries. It&rsquo;s not uncommon for an application to depend o n
24 dozens of different packages and libraries.</p>
25
26 <p>It&rsquo;s hard to understate how awesome this is. When you see stories of ti ny web
27 startups building a site in a few weeks that gets millions of users, the
28 only reason they can pull that off is because the open source community has
29 laid a feast of software at their feet.</p>
30
31 <p>But there&rsquo;s still no such thing as a free lunch. There&rsquo;s a challe nge to code
32 reuse, especially reusing code you don&rsquo;t maintain. When your app uses tons of
33 code being developed by other people, what happens when they change it? They
34 don&rsquo;t want to break your app, and you certainly don&rsquo;t either.</p>
35
36 <h2 id="a-name-and-a-number">A name and a number</h2>
37
38 <p>We solve this by <em>versioning</em>. When you depend on some piece of outsid e code,
39 you don&rsquo;t just say &ldquo;My app uses <code>widgets</code>.&rdquo; You say , &ldquo;My app uses
40 <code>widgets 2.0.5</code>.&rdquo; That combination of name and version number u niquely
41 identifies an <em>immutable</em> chunk of code. The people hacking on <code>widg ets</code> can
42 make all of the changes they want, but they promise to not touch any already
43 released versions. They can put out <code>2.0.6</code> or <code>3.0.0</code> and it won&rsquo;t affect you
44 one whit because the version you use is unchanged.</p>
45
46 <p>When you <em>do</em> want to get those changes, you can always point your app to a
47 newer version of <code>widgets</code> and you don&rsquo;t have to coordinate wit h those
48 developers to do it. So, problem solved, right?</p>
49
50 <h2 id="shared-dependencies-and-unshared-libraries">Shared dependencies and unsh ared libraries</h2>
51
52 <p>Well, no. Depending on specific versions works fine when your dependency <em> graph</em>
53 is really just a dependency <em>tree</em>. If your app depends on a bunch of stu ff, and
54 those things in turn have their own dependencies and so on, that all works fine
55 as long as none of those dependencies <em>overlap</em>.</p>
56
57 <p>But let&rsquo;s consider an example:</p>
58
59 <pre><code> myapp
60 / \
61 / \
62 widgets templates
63 \ /
64 \ /
65 collections
66 </code></pre>
67
68 <p>So your app uses <code>widgets</code> and <code>templates</code>, and <em>bot h</em> of those use
69 <code>collections</code>. This is called a <strong>shared dependency</strong>. N ow what happens when
70 <code>widgets</code> wants to use <code>collections 2.3.5</code> and <code>templ ates</code> wants
71 <code>collections 2.3.7</code>? What if they don&rsquo;t agree on a version?</p>
72
73 <p>One option is to just let the app use both
74 versions of <code>collections</code>. It will have two copies of the library at different
75 versions and <code>widgets</code> and <code>templates</code> will each get the o ne they want.</p>
76
77 <p>This is what <a href="https://npmjs.org/">npm</a> does for node.js. Would it work for Dart? Consider this
78 scenario:</p>
79
80 <ol>
81 <li><code>collections</code> defines some <code>Dictionary</code> class.</li>
82 <li><code>widgets</code> gets an instance of it from its copy of <code>collect ions</code> (<code>2.3.5</code>).
83 It then passes it up to <code>myapp</code>.</li>
84 <li><code>myapp</code> sends the dictionary over to <code>templates</code>.</l i>
85 <li>That in turn sends it down to <em>its</em> version of <code>collections</c ode> (<code>2.3.7</code>).</li>
86 <li>The method that takes it has a <code>Dictionary</code> type annotation for that object.</li>
87 </ol>
88
89 <p>As far as Dart is concerned, <code>collections 2.3.5</code> and <code>collect ions 2.3.7</code> are
90 entirely unrelated libraries. If you take an instance of class <code>Dictionary< /code> from
91 one and pass it to a method in the other, that&rsquo;s a completely different
92 <code>Dictionary</code> type. That means it will fail to match a <code>Dictionar y</code> type
93 annotation in the receiving library. Oops.</p>
94
95 <p>Because of this (and because of the headaches of trying to debug an app that
96 has multiple versions of things with the same name), we&rsquo;ve decided npm&rsq uo;s model
97 isn&rsquo;t a good fit.</p>
98
99 <h2 id="version-lock">Version lock</h2>
100
101 <p>Instead, when you depend on a package, your app will only use a single copy o f
102 that package. When you have a shared dependency, everything that depends on it
103 has to agree on which version to use. If they don&rsquo;t, you get an error.</p>
104
105 <p>That doesn&rsquo;t actually solve your problem though. When you <em>do</em> g et that error,
106 you need to be able to resolve it. So let&rsquo;s say you&rsquo;ve gotten yourse lf into
107 that situation in the above example. You want to use <code>widgets</code> and <c ode>templates</code>,
108 but they are using different versions of <code>collections</code>. What do you d o?</p>
109
110 <p>The answer is to try to upgrade one of those. <code>templates</code> wants
111 <code>collections 2.3.7</code>. Is there a later version of <code>widgets</code> that you can upgrade
112 to that works with that version?</p>
113
114 <p>In many cases, the answer will be &ldquo;no&rdquo;. Look at it from the persp ective of the
115 people developing <code>widgets</code>. They want to put out a new version with new changes
116 to <em>their</em> code, and they want as many people to be able to upgrade to it it as
117 possible. If they stick to their <em>current</em> version of <code>collections</ code> then anyone
118 who is using the current version <code>widgets</code> will be able to drop in th is new one
119 too.</p>
120
121 <p>If they were to upgrade <em>their</em> dependency on <code>collections</code> then everyone who
122 upgrades <code>widgets</code> would have to as well, <em>whether they want to or not.</em> That&rsquo;s
123 painful, so you end up with a disincentive to upgrade dependencies. That&rsquo;s
124 called <strong>version lock</strong>: everyone wants to move their dependencies forward, but
125 no one can take the first step because it forces everyone else to as well.</p>
126
127 <h2 id="version-constraints">Version constraints</h2>
128
129 <p>To solve version lock, we loosen the constraints that packages place on their
130 dependencies. If <code>widgets</code> and <code>templates</code> can both indica te a <em>range</em> of
131 versions for <code>collections</code> that they will work with, then that gives us enough
132 wiggle room to move our dependencies forward to newer versions. As long as there
133 is overlap in their ranges, we can still find a single version that makes them
134 both happy.</p>
135
136 <p>This is the model that <a href="http://gembundler.com/">bundler</a> follows, and is pub&rsquo;s
137 model too. When you add a dependency in your pubspec, you can specify a <em>rang e</em>
138 of versions that you can accept. If the pubspec for <code>widgets</code> looked like this:</p>
139
140 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">depe ndencies</span><span class="p-Indicator">:</span>
141 <span class="l-Scalar-Plain">collections</span><span class="p-Indicator">:</sp an> <span class="s">&#39;&gt;=2.3.5</span><span class="nv"> </span><span class=" s">&lt;2.4.0&#39;</span>
142 </code></pre></div>
143
144 <p>Then we could pick version <code>2.3.7</code> for <code>collections</code> an d then both <code>widgets</code>
145 and <code>templates</code> have their constraints satisfied by a single concrete version.</p>
146
147 <h2 id="semantic-versions">Semantic versions</h2>
148
149 <p>When you add a dependency to your package, you&rsquo;ll sometimes want to spe cify a
150 range of versions to allow. How do you know what range to pick? You need to
151 forward compatible, so ideally the range encompasses future versions that
152 haven&rsquo;t been released yet. But how do you know your package is going to wo rk
153 with some new version that doesn&rsquo;t even exist yet?</p>
154
155 <p>To solve that, you need to agree on what a version number <em>means</em>. Ima gine that
156 the developers of a package you depend on say, &ldquo;If we make any backwards
157 incompatible change, then we promise to increment the major version number.&rdqu o;
158 If you trust them, then if you know your package works with <code>2.5.7</code> o f theirs,
159 you can rely on it working all the way up to <code>3.0.0</code>. So you can set your range
160 like:</p>
161
162 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">depe ndencies</span><span class="p-Indicator">:</span>
163 <span class="l-Scalar-Plain">collections</span><span class="p-Indicator">:</sp an> <span class="s">&#39;&gt;=2.3.5</span><span class="nv"> </span><span class=" s">&lt;3.0.0&#39;</span>
164 </code></pre></div>
165
166 <p>To make this work, then, we need to come up with that set of promises.
167 Fortunately, other smart people have done the work of figuring this all out and
168 named it <a href="http://semver.org/"><em>semantic versioning</em></a>.</p>
169
170 <p>That describes the format of a version number, and the exact API behavioral
171 differences when you increment to a later version number. Pub requires versions
172 to be formatted that way, and to play well with the pub community, your package
173 should follow the semantics it specifies. You should assume that the packages
174 you depend on also follow it. (And if you find out they don&rsquo;t, let their
175 authors know!)</p>
176
177 <p>We&rsquo;ve got almost all of the pieces we need to deal with versioning and API
178 evolution now. Let&rsquo;s see how they play together and what pub does.</p>
179
180 <h2 id="constraint-solving">Constraint solving</h2>
181
182 <p>When you define your package, you list its
183 <a href="glossary.html#immediate-dependency"><strong>immediate dependencies</str ong></a>&mdash;the
184 packages it itself uses. For each one, you specify the range of versions it
185 allows. Each of those dependent packages may in turn have their own
186 dependencies (called
187 <a href="glossary.html#transitive-dependency"><strong>transitive dependencies</s trong></a>. Pub will
188 traverse these and build up the entire deep dependency graph for your app.</p>
189
190 <p>For each package in the graph, pub looks at everything that depends on it. It
191 gathers together all of their version constraints and tries to simultaneously
192 solve them. (Basically, it intersects their ranges.) Then it looks at the
193 actual versions that have been released for that package and selects the best
194 (most recent) one that meets all of those constraints.</p>
195
196 <p>For example, let&rsquo;s say our dependency graph contains <code>collections< /code>, and three
197 packages depend on it. Their version constraints are:</p>
198
199 <pre><code>&gt;=1.7.0
200 &gt;=1.4.0 &lt;2.0.0
201 &lt;1.9.0
202 </code></pre>
203
204 <p>The developers of <code>collections</code> have released these versions of it :</p>
205
206 <pre><code>1.7.0
207 1.7.1
208 1.8.0
209 1.8.1
210 1.8.2
211 1.9.0
212 </code></pre>
213
214 <p>The highest version number that fits in all of those ranges is <code>1.8.2</c ode>, so pub
215 picks that. That means your app <em>and every package your app uses</em> will al l use
216 <code>collections 1.8.2</code>.</p>
217
218 <h2 id="constraint-context">Constraint context</h2>
219
220 <p>The fact that selecting a package version takes into account <em>every</em> p ackage
221 that depends on it has an important consequence: <em>the specific version that
222 will be selected for a package is a global property of the app using that
223 package.</em></p>
224
225 <p>I&rsquo;ll walk through an example so you can see what this means. Let&rsquo; s say we have
226 two apps. Here are their pubspecs:</p>
227
228 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name </span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">my_app</s pan>
229 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa n>
230 <span class="l-Scalar-Plain">widgets</span><span class="p-Indicator">:</span>
231 </code></pre></div>
232
233 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name </span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">other_app </span>
234 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa n>
235 <span class="l-Scalar-Plain">widgets</span><span class="p-Indicator">:</span>
236 <span class="l-Scalar-Plain">collections</span><span class="p-Indicator">:</sp an> <span class="s">&#39;&lt;1.5.0&#39;</span>
237 </code></pre></div>
238
239 <p>They both depend on <code>widgets</code>, whose pubspec is:</p>
240
241 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name </span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">widgets</ span>
242 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa n>
243 <span class="l-Scalar-Plain">collections</span><span class="p-Indicator">:</sp an> <span class="s">&#39;&gt;=1.0.0</span><span class="nv"> </span><span class=" s">&lt;2.0.0&#39;</span>
244 </code></pre></div>
245
246 <p>The <code>other_app</code> package uses depends directly on <code>collections </code> itself. The
247 interesting part is that it happens to have a different version constraint on
248 it than <code>widgets</code> does.</p>
249
250 <p>What this means is that you can&rsquo;t just look at the <code>widgets</code> package in
251 isolation to figure out what version of <code>collections</code> it will use. It depends
252 on the context. In <code>my_app</code>, <code>widgets</code> will be using <code >collections 1.9.9</code>. But
253 in <code>other_app</code>, <code>widgets</code> will get saddled with <code>coll ections 1.4.9</code> because of
254 the <em>other</em> constraint that <code>otherapp</code> places on it.</p>
255
256 <p>This is why each app gets its own &ldquo;packages&rdquo; directory: The concr ete version
257 selected for each package depends on the entire dependency graph of the
258 containing app.</p>
259
260 <h2 id="lockfiles">Lockfiles</h2>
261
262 <p>So once pub has solved your app&rsquo;s version constraints, then what? The e nd
263 result is a complete list of every package that your app depends on either
264 directly or indirectly and the best version of that package that will work with
265 your app&rsquo;s constraints.</p>
266
267 <p>Pub takes that and writes it out to a <strong>lockfile</strong> in your app&r squo;s directory
268 called <code>pubspec.lock</code>. When pub builds the &ldquo;packages&rdquo; dir ectory your app, it
269 uses the lockfile to know what versions of each package to pull in. (And if
270 you&rsquo;re curious to see what versions it selected, you can read the lockfile to
271 find out.)</p>
272
273 <p>The next important thing pub does is it <em>stops touching the lockfile</em>. Once
274 you&rsquo;ve got a lockfile for your app, pub won&rsquo;t mess with it until you tell it to.
275 This is important. It means you won&rsquo;t spontanteously start using new versi ons
276 of random packages in your app without intending to. Once your app is locked,
277 it stays locked until you manually tell it to update the lockfile.</p>
278
279 <p>If your package is for an app, you take your lockfile <em>check that bad boy
280 into your source control system!</em> That way, everyone on your team will be us ing
281 the exact same versions of every dependency when they hack on your app. You&rsqu o;ll
282 also use this when you deploy your app so you can ensure that your production
283 servers are using the exact same packages that you&rsquo;re developing with.</p>
284
285 <h2 id="when-things-go-wrong">When things go wrong</h2>
286
287 <p>Of course, all of this presumes that your dependency graph is perfect and
288 flawless. Oh, to be so fortunate. Even with version ranges and pub&rsquo;s const raint
289 solving and semantic versioning, you can never be entirely spared from the
290 dangers of version hell.</p>
291
292 <p>There are a couple of problems you can run into:</p>
293
294 <h3 id="you-can-have-disjoint-constraints">You can have disjoint constraints</h3 >
295
296 <p>Lets say your app uses <code>widgets</code> and
297 <code>templates</code> and both use <code>collections</code>. But <code>widgets< /code> asks for a version
298 of it between <code>1.0.0</code> and <code>2.0.0</code> and <code>templates</cod e> wants something
299 between <code>3.0.0</code> and <code>4.0.0</code>. Those ranges don&rsquo;t even overlap. There&rsquo;s no
300 possible version that would work.</p>
301
302 <h3 id="you-can-have-ranges-that-dont-contain-a-released-version">You can have r anges that don&rsquo;t contain a released version</h3>
303
304 <p>Let&rsquo;s say after
305 putting all of the constraints on a shared dependency together, you&rsquo;re
306 left with the narrow range of <code>&gt;=1.2.4 &lt;1.2.6</code>. It&rsquo;s not an empty range.
307 If there was a version <code>1.2.4</code> of the dependency, you&rsquo;d be gold en. But maybe
308 they never released that and instead when straight from <code>1.2.3</code> to <c ode>1.3.0</code>.
309 You&rsquo;ve got a range but nothing exists inside it.</p>
310
311 <h3 id="you-can-have-an-unstable-graph">You can have an unstable graph</h3>
312
313 <p>This is, by far, the hairiest part of
314 pub&rsquo;s version solving process. I&rsquo;ve described the process as &ldquo; build up the
315 dependency graph and then solve all of the constraints and pick versions&rdquo;.
316 But it doesn&rsquo;t actually work that way. How could you build up the <em>whol e</em>
317 dependency graph before you&rsquo;ve picked <em>any</em> versions? <em>The pubsp ecs
318 themselves are version-specific</em>. Different versions of the same package
319 may have different sets of dependencies.</p>
320
321 <p>As you&rsquo;re selecting versions of packages, they are changing the shape o f
322 the dependency graph itself. As the graph changes, that may change
323 constraints, which can cause you to select different versions, and then you
324 go right back around in a circle.</p>
325
326 <p>Sometimes this process never settles down into a stable solution. Gaze into
327 the abyss:</p>
328
329 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name </span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">my_app</s pan>
330 <span class="l-Scalar-Plain">version</span><span class="p-Indicator">:</span> <s pan class="l-Scalar-Plain">0.0.0</span>
331 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa n>
332 <span class="l-Scalar-Plain">yin</span><span class="p-Indicator">:</span> <spa n class="s">&#39;&gt;=1.0.0&#39;</span>
333 </code></pre></div>
334
335 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name </span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">yin</span >
336 <span class="l-Scalar-Plain">version</span><span class="p-Indicator">:</span> <s pan class="l-Scalar-Plain">1.0.0</span>
337 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa n>
338 </code></pre></div>
339
340 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name </span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">yin</span >
341 <span class="l-Scalar-Plain">version</span><span class="p-Indicator">:</span> <s pan class="l-Scalar-Plain">2.0.0</span>
342 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa n>
343 <span class="l-Scalar-Plain">yang</span><span class="p-Indicator">:</span> <sp an class="s">&#39;1.0.0&#39;</span>
344 </code></pre></div>
345
346 <div class="highlight"><pre><code class="yaml"><span class="l-Scalar-Plain">name </span><span class="p-Indicator">:</span> <span class="l-Scalar-Plain">yang</spa n>
347 <span class="l-Scalar-Plain">version</span><span class="p-Indicator">:</span> <s pan class="l-Scalar-Plain">1.0.0</span>
348 <span class="l-Scalar-Plain">dependencies</span><span class="p-Indicator">:</spa n>
349 <span class="l-Scalar-Plain">yin</span><span class="p-Indicator">:</span> <spa n class="s">&#39;1.0.0&#39;</span>
350 </code></pre></div>
351
352 <p>In all of these cases, there is no set of concrete versions that will work fo r
353 your app, and when this happens pub will report an error and tell you what&rsquo ;s
354 going on. It definitely will not try to leave you in some weird state where you
355 think things can work but won&rsquo;t.</p>
356
357 <h2 id="summary">Summary</h2>
358
359 <p>Wow, that&rsquo;s a lot to get through. Here&rsquo;s the important bits:</p>
360
361 <ul>
362 <li>Code reuse is great, but in order to let developers move quickly, packages
363 need to be able to evolve independently.</li>
364 <li>Versioning is how you enable that. But depending on single concrete versio ns
365 is too precise and with shared dependencies leads to version lock.</li>
366 <li>To cope with that, you depend on <em>ranges</em> of versions. Pub will the n walk
367 your dependency graph and pick the best versions for you. If it can&rsquo;t, it
368 tells you.</li>
369 <li>Once your app has a solid set of versions for its dependencies, that gets
370 pinned down in a <em>lockfile</em>. That ensures that every machine your app is
371 on is using the same versions of all of its dependencies.</li>
372 </ul>
OLDNEW
« no previous file with comments | « app/views/doc/pubspec.html ('k') | app/views/index.mustache » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698