A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang=fr>
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset=utf-8>
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name=viewport content="width=device-width,minimum-scale=1,initial-scale=1,shrink-to-fit=no">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>The Cost of Frameworks (archive) — David Larlet</title>
  13. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  14. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons/apple-touch-icon.png">
  15. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons/favicon-32x32.png">
  16. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons/favicon-16x16.png">
  17. <link rel="manifest" href="/manifest.json">
  18. <link rel="mask-icon" href="/static/david/icons/safari-pinned-tab.svg" color="#5bbad5">
  19. <link rel="shortcut icon" href="/static/david/icons/favicon.ico">
  20. <meta name="apple-mobile-web-app-title" content="David Larlet">
  21. <meta name="application-name" content="David Larlet">
  22. <meta name="msapplication-TileColor" content="#da532c">
  23. <meta name="msapplication-config" content="/static/david/icons/browserconfig.xml">
  24. <meta name="theme-color" content="#f0f0ea">
  25. <!-- That good ol' feed, subscribe :p. -->
  26. <link rel=alternate type="application/atom+xml" title=Feed href="/david/log/">
  27. <meta name="robots" content="noindex, nofollow">
  28. <meta content="origin-when-cross-origin" name="referrer">
  29. <!-- Canonical URL for SEO purposes -->
  30. <link rel="canonical" href="https://aerotwist.com/blog/the-cost-of-frameworks/">
  31. <style>
  32. /* http://meyerweb.com/eric/tools/css/reset/ */
  33. html, body, div, span,
  34. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  35. a, abbr, address, big, cite, code,
  36. del, dfn, em, img, ins,
  37. small, strike, strong, tt, var,
  38. dl, dt, dd, ol, ul, li,
  39. fieldset, form, label, legend,
  40. table, caption, tbody, tfoot, thead, tr, th, td,
  41. article, aside, canvas, details, embed,
  42. figure, figcaption, footer, header, hgroup,
  43. menu, nav, output, ruby, section, summary,
  44. time, mark, audio, video {
  45. margin: 0;
  46. padding: 0;
  47. border: 0;
  48. font-size: 100%;
  49. font: inherit;
  50. vertical-align: baseline;
  51. }
  52. /* HTML5 display-role reset for older browsers */
  53. article, aside, details, figcaption, figure,
  54. footer, header, hgroup, menu, nav, section { display: block; }
  55. body { line-height: 1; }
  56. blockquote, q { quotes: none; }
  57. blockquote:before, blockquote:after,
  58. q:before, q:after {
  59. content: '';
  60. content: none;
  61. }
  62. table {
  63. border-collapse: collapse;
  64. border-spacing: 0;
  65. }
  66. /* http://practicaltypography.com/equity.html */
  67. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  68. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  69. @font-face {
  70. font-family: 'EquityTextB';
  71. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  72. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  73. font-weight: 300;
  74. font-style: normal;
  75. font-display: swap;
  76. }
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: italic;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  89. font-weight: 700;
  90. font-style: normal;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'ConcourseT3';
  95. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  96. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  97. font-weight: 300;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  102. body {
  103. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  104. font-family: "EquityTextB", Palatino, serif;
  105. background-color: #f0f0ea;
  106. color: #07486c;
  107. font-kerning: normal;
  108. -moz-osx-font-smoothing: grayscale;
  109. -webkit-font-smoothing: subpixel-antialiased;
  110. text-rendering: optimizeLegibility;
  111. font-variant-ligatures: common-ligatures contextual;
  112. font-feature-settings: "kern", "liga", "clig", "calt";
  113. }
  114. pre, code, kbd, samp, var, tt {
  115. font-family: 'TriplicateT4c', monospace;
  116. }
  117. em {
  118. font-style: italic;
  119. color: #323a45;
  120. }
  121. strong {
  122. font-weight: bold;
  123. color: black;
  124. }
  125. nav {
  126. background-color: #323a45;
  127. color: #f0f0ea;
  128. display: flex;
  129. justify-content: space-around;
  130. padding: 1rem .5rem;
  131. }
  132. nav:last-child {
  133. border-bottom: 1vh solid #2d7474;
  134. }
  135. nav a {
  136. color: #f0f0ea;
  137. }
  138. nav abbr {
  139. border-bottom: 1px dotted white;
  140. }
  141. h1 {
  142. border-top: 1vh solid #2d7474;
  143. border-bottom: .2vh dotted #2d7474;
  144. background-color: #e3e1e1;
  145. color: #323a45;
  146. text-align: center;
  147. padding: 5rem 0 4rem 0;
  148. width: 100%;
  149. font-family: 'ConcourseT3';
  150. display: flex;
  151. flex-direction: column;
  152. }
  153. h1.single {
  154. padding-bottom: 10rem;
  155. }
  156. h1 span {
  157. position: absolute;
  158. top: 1vh;
  159. left: 20%;
  160. line-height: 0;
  161. }
  162. h1 span a {
  163. line-height: 1.7;
  164. padding: 1rem 1.2rem .6rem 1.2rem;
  165. border-radius: 0 0 6% 6%;
  166. background: #2d7474;
  167. font-size: 1.3rem;
  168. color: white;
  169. text-decoration: none;
  170. }
  171. h2 {
  172. margin: 4rem 0 1rem;
  173. border-top: .2vh solid #2d7474;
  174. padding-top: 1vh;
  175. }
  176. h3 {
  177. text-align: center;
  178. margin: 3rem 0 .75em;
  179. }
  180. hr {
  181. height: .4rem;
  182. width: .4rem;
  183. border-radius: .4rem;
  184. background: #07486c;
  185. margin: 2.5rem auto;
  186. }
  187. time {
  188. display: bloc;
  189. margin-left: 0 !important;
  190. }
  191. ul, ol {
  192. margin: 2rem;
  193. }
  194. ul {
  195. list-style-type: square;
  196. }
  197. a {
  198. text-decoration-skip-ink: auto;
  199. text-decoration-thickness: 0.05em;
  200. text-underline-offset: 0.09em;
  201. }
  202. article {
  203. max-width: 50rem;
  204. display: flex;
  205. flex-direction: column;
  206. margin: 2rem auto;
  207. }
  208. article.single {
  209. border-top: .2vh dotted #2d7474;
  210. margin: -6rem auto 1rem auto;
  211. background: #f0f0ea;
  212. padding: 2rem;
  213. }
  214. article p:last-child {
  215. margin-bottom: 1rem;
  216. }
  217. p {
  218. padding: 0 .5rem;
  219. margin-left: 3rem;
  220. }
  221. p + p,
  222. figure + p {
  223. margin-top: 2rem;
  224. }
  225. blockquote {
  226. background-color: #e3e1e1;
  227. border-left: .5vw solid #2d7474;
  228. display: flex;
  229. flex-direction: column;
  230. align-items: center;
  231. padding: 1rem;
  232. margin: 1.5rem;
  233. }
  234. blockquote cite {
  235. font-style: italic;
  236. }
  237. blockquote p {
  238. margin-left: 0;
  239. }
  240. figure {
  241. border-top: .2vh solid #2d7474;
  242. background-color: #e3e1e1;
  243. text-align: center;
  244. padding: 1.5rem 0;
  245. margin: 1rem 0 0;
  246. font-size: 1.5rem;
  247. width: 100%;
  248. }
  249. figure img {
  250. max-width: 250px;
  251. max-height: 250px;
  252. border: .5vw solid #323a45;
  253. padding: 1px;
  254. }
  255. figcaption {
  256. padding: 1rem;
  257. line-height: 1.4;
  258. }
  259. aside {
  260. display: flex;
  261. flex-direction: column;
  262. background-color: #e3e1e1;
  263. padding: 1rem 0;
  264. border-bottom: .2vh solid #07486c;
  265. }
  266. aside p {
  267. max-width: 50rem;
  268. margin: 0 auto;
  269. }
  270. /* https://fvsch.com/code/css-locks/ */
  271. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  272. font-size: 1rem;
  273. line-height: calc( 1.5em + 0.2 * 1rem );
  274. }
  275. h1 {
  276. font-size: 1.9rem;
  277. line-height: calc( 1.2em + 0.2 * 1rem );
  278. }
  279. h2 {
  280. font-size: 1.6rem;
  281. line-height: calc( 1.3em + 0.2 * 1rem );
  282. }
  283. h3 {
  284. font-size: 1.35rem;
  285. line-height: calc( 1.4em + 0.2 * 1rem );
  286. }
  287. @media (min-width: 20em) {
  288. /* The (100vw - 20rem) / (50 - 20) part
  289. resolves to 0-1rem, depending on the
  290. viewport width (between 20em and 50em). */
  291. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  292. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  293. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  294. margin-left: 0;
  295. }
  296. h1 {
  297. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  298. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  299. }
  300. h2 {
  301. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  302. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  303. }
  304. h3 {
  305. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. }
  309. @media (min-width: 50em) {
  310. /* The right part of the addition *must* be a
  311. rem value. In this example we *could* change
  312. the whole declaration to font-size:2.5rem,
  313. but if our baseline value was not expressed
  314. in rem we would have to use calc. */
  315. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  316. font-size: calc( 1rem + .6 * 1rem );
  317. line-height: 1.5em;
  318. }
  319. p, li, pre, details {
  320. margin-left: 3rem;
  321. }
  322. h1 {
  323. font-size: calc( 1.9rem + 1.5 * 1rem );
  324. line-height: 1.2em;
  325. }
  326. h2 {
  327. font-size: calc( 1.5rem + 1.5 * 1rem );
  328. line-height: 1.3em;
  329. }
  330. h3 {
  331. font-size: calc( 1.35rem + 1.5 * 1rem );
  332. line-height: 1.4em;
  333. }
  334. figure img {
  335. max-width: 500px;
  336. max-height: 500px;
  337. }
  338. }
  339. figure.unsquared {
  340. margin-bottom: 1.5rem;
  341. }
  342. figure.unsquared img {
  343. height: inherit;
  344. }
  345. @media print {
  346. body { font-size: 100%; }
  347. a:after { content: " (" attr(href) ")"; }
  348. a, a:link, a:visited, a:after {
  349. text-decoration: underline;
  350. text-shadow: none !important;
  351. background-image: none !important;
  352. background: white;
  353. color: black;
  354. }
  355. abbr[title] { border-bottom: 0; }
  356. abbr[title]:after { content: " (" attr(title) ")"; }
  357. img { page-break-inside: avoid; }
  358. @page { margin: 2cm .5cm; }
  359. h1, h2, h3 { page-break-after: avoid; }
  360. p3 { orphans: 3; widows: 3; }
  361. img {
  362. max-width: 250px !important;
  363. max-height: 250px !important;
  364. }
  365. nav, aside { display: none; }
  366. }
  367. ul.with_columns {
  368. column-count: 1;
  369. }
  370. @media (min-width: 20em) {
  371. ul.with_columns {
  372. column-count: 2;
  373. }
  374. }
  375. @media (min-width: 50em) {
  376. ul.with_columns {
  377. column-count: 3;
  378. }
  379. }
  380. ul.with_two_columns {
  381. column-count: 1;
  382. }
  383. @media (min-width: 20em) {
  384. ul.with_two_columns {
  385. column-count: 1;
  386. }
  387. }
  388. @media (min-width: 50em) {
  389. ul.with_two_columns {
  390. column-count: 2;
  391. }
  392. }
  393. .gallery {
  394. display: flex;
  395. flex-wrap: wrap;
  396. justify-content: space-around;
  397. }
  398. .gallery figure img {
  399. margin-left: 1rem;
  400. margin-right: 1rem;
  401. }
  402. .gallery figure figcaption {
  403. font-family: 'ConcourseT3'
  404. }
  405. footer {
  406. font-family: 'ConcourseT3';
  407. display: flex;
  408. flex-direction: column;
  409. border-top: 3px solid white;
  410. padding: 4rem 0;
  411. background-color: #07486c;
  412. color: white;
  413. }
  414. footer > * {
  415. max-width: 50rem;
  416. margin: 0 auto;
  417. }
  418. footer a {
  419. color: #f1c40f;
  420. }
  421. footer .avatar {
  422. width: 200px;
  423. height: 200px;
  424. border-radius: 50%;
  425. float: left;
  426. -webkit-shape-outside: circle();
  427. shape-outside: circle();
  428. margin-right: 2rem;
  429. padding: 2px 5px 5px 2px;
  430. background: white;
  431. border-left: 1px solid #f1c40f;
  432. border-top: 1px solid #f1c40f;
  433. border-right: 5px solid #f1c40f;
  434. border-bottom: 5px solid #f1c40f;
  435. }
  436. </style>
  437. <h1>
  438. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  439. The Cost of Frameworks (archive)
  440. <time>Pour la pérennité des contenus liés. Non-indexé, retrait sur simple email.</time>
  441. </h1>
  442. <section>
  443. <article>
  444. <h3><a href="https://aerotwist.com/blog/the-cost-of-frameworks/">Source originale du contenu</a></h3>
  445. <p><strong>Update: Nov 16th 2015 - Added an extra row in the table for React under production conditions. The good news: it’s 3x slower than vanilla, yes, but in actual terms I’d say it’s fast for TodoMVC! The Polymer TodoMVC sample was also updated to version 1.2.2 today, and that, too, is faster.</strong></p>
  446. <p>If you prefer watching to reading, here’s the video of the talk (you can also <a href="https://speakerdeck.com/paullewis/framework-here-its-the-bestestest">get the slides</a>, too, if you like):</p>
  447. <iframe width="100%" height="420" src="//www.youtube.com/embed/_yCz1TA0EL4?vq=hd720" frameborder="0" allowfullscreen="allowfullscreen" style="margin-bottom: 20px; box-shadow: 0 2px 2px rgba(0,0,0,0.3)">
  448. </iframe>
  449. <p>If you prefer reading to watching, well, keep reading…</p>
  450. <h2 id="the-benefits-of-frameworks">The benefits of frameworks</h2>
  451. <p>Earlier in the year I wrote about <a href="https://aerotwist.com/react-plus-performance-equals-what/">React’s performance characteristics</a> as the tree size it has to manage gets larger (TL;DR the bigger the tree, the more computation work it has to do). The responses I got to that post really varied, from appreciative and constructive to, well, the other end of the spectrum. The responses gave me some additional insight, though, which I think applies to all frameworks (to a greater or lesser extent), and which I’ll now try and summarize:</p>
  452. <ul>
  453. <li><strong>Frameworks are fun to use</strong>. I don’t think I know any developer who actively seeks out a more challenging and less fun experience! And yes, absolutely, ergonomics <em>are</em> important.</li>
  454. <li><strong>Frameworks make it quicker to build an MVP</strong>. This one is important, particularly for smaller, fledgling organizations or companies. Having a fast bootstrap may be the difference between shipping and not shipping.</li>
  455. <li><strong>Frameworks work around lumps / bugs in the platform</strong>. I think this is less of an issue these days (though not irrelevant) as browsers have rallied around standards very well in the past few years. Certainly this is less painful compared to my early career, where we were dealing with IEs 5 and 6, and early versions of Firefox, Safari and, of course, Chrome.</li>
  456. <li><strong>Knowing [insert framework] means I get paid.</strong> If you’re looking to get hired, saying that you know the latest and greatest framework(s) may be the difference maker.</li>
  457. </ul>
  458. <blockquote>
  459. <p>The key message I heard over and over, sometimes explicitly, and often implicitly, is that ergonomics are the most important factor for many developers.</p>
  460. </blockquote>
  461. <p>The key message I heard over and over, sometimes explicitly, and often implicitly, is that ergonomics are <em>the most important factor</em> for many developers. If I can paraphrase, the sentiment was “if I have an easier time I’ll build something better for my users”. At face value that sounds appealing, but I think it misses a lot of important points.</p>
  462. <h2 id="developer-ergonomics-vs-user-needs">Developer Ergonomics vs User Needs</h2>
  463. <p>Users have needs, too, though. Here are a few of them that come to mind:</p>
  464. <ul>
  465. <li><strong>Sites and apps should load quickly.</strong></li>
  466. <li><strong>They should have smooth interactions.</strong></li>
  467. <li><strong>They shouldn’t slow down my phone.</strong></li>
  468. <li><strong>They shouldn’t crash.</strong></li>
  469. <li><strong>They should have features I want.</strong></li>
  470. </ul>
  471. <p>So immediately we’re into a potential trade-off situation: on the one hand we have our convenience, our ergonomics, and on the other hand we have our users’ needs. And we have to trade, whether we like it or not, since frameworks aren’t free.</p>
  472. <h2 id="a-quick-side-step">A quick side step</h2>
  473. <p>At this point I’m going to park libraries, and leave them out of this discussion. The reason is that, in my view at least, libraries can be swapped out and replaced if they prove problematic. Don’t like the way a library does date formatting? No problem, just switch it for something else. Frameworks, on the other hand, tend to be a lot more difficult to swap, often requiring a rebuild of the app in question. And, by their nature, they’re a lot bigger and more involved.</p>
  474. <h2 id="the-cost-of-code">The cost of code</h2>
  475. <p>All code has costs, but I think there are particular costs that are unique to frameworks.</p>
  476. <figure>
  477. <img src="https://aerotwist.com/static/blog/the-cost-of-frameworks/deprecated.jpg" alt="A deprecation warning." />
  478. <figcaption>That magical moment where an framework tells you something is deprecated. Apparently it was superseded by Java. Weird.</figcaption>
  479. </figure>
  480. <ul>
  481. <li><strong>Learning it.</strong> You have to take time out to learn any new framework: what it means to write “idiomatic” code for that framework; its patterns, practices, and ways of doing things. This is no different to the web platform itself. There’s no free ride, you’re just swapping one set of issues for another.</li>
  482. <li><strong>Re-learning it.</strong> One day you’re just bimbling along, developing your app, and in the console you see a warning telling you that there’s been a deprecation, and that you need to update your code. That often requires figuring out what the latest version of the framework needs you to do differently, and that may also mean updating projects that shipped with older versions of the framework.</li>
  483. <li><strong>Debugging it.</strong> Frameworks, like any software, have bugs. That’s to be expected, and it wouldn’t be any different if you went vanilla and wrote your own. Debugging a framework, however, often ranges from difficult to impossible. If you find the issue, you may have to keep your own frankensteined version around with a monkey patch applied until the real fix lands in the framework’s production build.</li>
  484. </ul>
  485. <p>Aside from the developer costs, there are costs to the user, too:</p>
  486. <ul>
  487. <li><strong>Time.</strong> How long does it take to bootstrap the thing we sent down the wire?</li>
  488. <li><strong>Bandwidth.</strong> How much bandwidth does it use up?</li>
  489. <li><strong>CPU usage (battery).</strong> How CPU intensive is it? CPU utilization is going to correlate to battery usage: burn the CPU and the battery will get drained.</li>
  490. <li><strong>Frame rate.</strong> How well does the code update the screen? (We know that <a href="https://paul.kinlan.me/what-news-readers-want/">users like smooth interactions</a>.)</li>
  491. <li><strong>Memory usage.</strong> How much memory does it use? Will it cause other apps to be evicted from memory? Perhaps even crash a tab?</li>
  492. </ul>
  493. <h2 id="bringing-data-to-the-table">Bringing data to the table</h2>
  494. <p>So with all that in mind, I wanted to start off a conversation about measuring some of those costs, and my hope is that together we can build up more of a picture about the trade-offs we choose to make.</p>
  495. <p>I decided to take a look at the bootstrapping time for frameworks on mobile. The theory being that, given a like-for-like app, one should be able to see how long it takes each framework to boot. I opted to use <strong>TodoMVC</strong> for the test, because in my view it’s an MVP web app, and from a user’s point of view, each variant is functionally identical.</p>
  496. <figure>
  497. <img src="https://aerotwist.com/static/blog/the-cost-of-frameworks/todomvc.jpg" alt="TodoMVC." />
  498. <figcaption>TodoMVC: source of my bootstrapping test.</figcaption>
  499. </figure>
  500. <h3 id="bootstrapping--time-to-interactive">Bootstrapping / Time-to-interactive</h3>
  501. <p>Of the costs listed above, I decided to take a look at the <strong>Time</strong>, <strong>Bandwidth</strong>, and <strong>CPU usage</strong> for booting up some frameworks on a Nexus 5 and iPhone 5S.</p>
  502. <figure>
  503. <img src="https://aerotwist.com/static/blog/the-cost-of-frameworks/time-to-interactive.jpg" alt="Measuring time to interactive." />
  504. <figcaption>Time to interactive: measuring how long it takes for an app to get to a state where someone can interact with it.</figcaption>
  505. </figure>
  506. <p>I decided to measure the time it takes to <strong>evaluate and execute each framework’s initial payload of JavaScript</strong>, and the processing and setup of the initial data set into a model. I decided to ignore the cost of styles, layout, paint and so on since they shouldn’t vary all that much between the different TodoMVC implementations. I was also not testing transfer time.</p>
  507. <h3 id="methodology">Methodology</h3>
  508. <p>I used <a href="https://webpagetest.org">WebPagetest</a> to load the pages on a Nexus 5, and I requested a timeline file for each run, which I then passed to <a href="https://aerotwist.com/bigrig/">Big Rig</a> for processing. The iPhone results I did myself, and had to tally up manually, because unfortunately Safari’s JavaScript profiling doesn’t seem to support any timeline / trace file export.</p>
  509. <figure>
  510. <img src="https://aerotwist.com/static/blog/the-cost-of-frameworks/bigrig.jpg" alt="Using Big Rig to measure TodoMVC bootup time." />
  511. <figcaption>Using Big Rig to quickly assess the TodoMVC bootup time per framework.</figcaption>
  512. </figure>
  513. <p>This, incidentally, is why I built <a href="https://aerotwist.com/bigrig">Big Rig</a>. I wanted to make it easier for all of us to start processing this kind of data quickly.</p>
  514. <h3 id="repeating-the-test">Repeating the test</h3>
  515. <p>If you want to do your own test like this, at least for a device running Chrome, you should do the following:</p>
  516. <ul>
  517. <li>Install the Big Rig CLI: <code>npm install -g bigrig</code>.</li>
  518. <li>Go to <a href="http://webpagetest.org">WebPagetest</a>.</li>
  519. <li>Set the location to Dulles.</li>
  520. <li>Set the browser to Nexus 5 - Chrome (or Beta/Dev).</li>
  521. <li>Under the Chrome settings, check “Capture Dev Tools Timeline”.</li>
  522. <li>Run the test.</li>
  523. <li>Download the Timeline file on the left hand side of the results.</li>
  524. <li>Run <code>bigrig --file /path/to/timeline.json --pretty-print</code>.</li>
  525. </ul>
  526. <p>If you prefer, you can watch me getting a Timeline file from WebPagetest:</p>
  527. <iframe width="100%" height="420" src="//www.youtube.com/embed/LFOCkLqPklg?vq=hd720" frameborder="0" allowfullscreen="allowfullscreen" style="margin-bottom: 20px; box-shadow: 0 2px 2px rgba(0,0,0,0.3)">
  528. </iframe>
  529. <h2 id="the-results">The results</h2>
  530. <p>Here’s how the various frameworks stacked up in my tests:</p>
  531. <table class="left-highlight">
  532. <thead>
  533. <tr>
  534. <th>Framework</th>
  535. <th>Size</th>
  536. <th>Bootstrap time N5<sup>1,3</sup></th>
  537. <th>Bootstrap time iPhone 5S<sup>2,3</sup></th>
  538. </tr>
  539. </thead>
  540. <tbody>
  541. <tr>
  542. <td>Polymer v1.1.4</td>
  543. <td>41KB<sup>5</sup></td>
  544. <td>409ms</td>
  545. <td>233ms</td>
  546. </tr>
  547. <tr>
  548. <td>Polymer v1.2.2</td>
  549. <td>47KB<sup>5</sup></td>
  550. <td>155ms</td>
  551. <td>232ms</td>
  552. </tr>
  553. <tr>
  554. <td>AngularJS v1.4.3</td>
  555. <td>324KB</td>
  556. <td>518ms</td>
  557. <td>307ms</td>
  558. </tr>
  559. <tr>
  560. <td>React v0.13.3 [JSX not transformed]</td>
  561. <td>311KB</td>
  562. <td>1,201ms</td>
  563. <td>1,463ms</td>
  564. </tr>
  565. <tr>
  566. <td>React v0.13.3 [JSX transformed via Babel]<sup>4</sup></td>
  567. <td>162KB</td>
  568. <td>509ms</td>
  569. <td>282ms</td>
  570. </tr>
  571. <tr>
  572. <td>React v0.13.3 [JSX transformed; prod build]<sup>4, 6</sup></td>
  573. <td>160KB</td>
  574. <td>174ms</td>
  575. <td>118ms</td>
  576. </tr>
  577. <tr>
  578. <td>Backbone v1.2.2 [inc. jQuery &amp; Underscore]</td>
  579. <td>139KB</td>
  580. <td>248ms</td>
  581. <td>168ms</td>
  582. </tr>
  583. <tr>
  584. <td>Ember v1.10.0-beta.3</td>
  585. <td>580KB</td>
  586. <td>1,992ms</td>
  587. <td>1,440ms</td>
  588. </tr>
  589. <tr>
  590. <td>Vanilla</td>
  591. <td>16KB</td>
  592. <td>50ms</td>
  593. <td>33ms</td>
  594. </tr>
  595. </tbody>
  596. </table>
  597. <ol>
  598. <li>Tests done on a Nexus 5 running Chrome 47.</li>
  599. <li>Tests done on an iPhone 5S running Safari 9.</li>
  600. <li>All bootstrapping time includes handling initial todo list data.</li>
  601. <li>JS Transformer stripped; JSX files transformed via Babel.</li>
  602. <li>Excludes Web Components Polyfill (38KB).</li>
  603. <li>React switched for minified production build.</li>
  604. </ol>
  605. <p>For me the results are pretty clear: <strong>there appears to be a pretty hefty tax to using Frameworks on mobile, especially compared to writing vanilla JavaScript</strong>. The fastest is React (under production conditions), which is awesome, but it’s still 3x slower than Vanilla (and I <a href="https://aerotwist.com/react-plus-performance-equals-what/">still have concerns over its scaling properties</a>.)</p>
  606. <p>To make things super clear, here are a couple of notes:</p>
  607. <ul>
  608. <li><strong>TodoMVC doesn’t JSX Transform React, so I did.</strong> The reason there are three entries for React is that the TodoMVC example doesn’t transform JSX, and instead includes the JSX Transformer library. To make things fairer (because the runtime JSX transformation is expensive!), I made a more ‘productionized’ version of the example, and tested that, too. The downside is that it’s not the minified version of React, which is different again. So I also switched out the build of React for the production version, and that’s the third build in the table.</li>
  609. <li><strong>These times do <em>not</em> include transfer time.</strong> The only thing I was measuring was the time in JavaScript to bootstrap the framework and get the initial view built. In fact, <em>some of the frameworks in TodoMVC aren’t minified</em>. For a more discussion on framework transfer sizes, <a href="https://www.filamentgroup.com/lab/mv-initial-load-times.html">check out the Filament Group’s post from last year</a>.</li>
  610. </ul>
  611. <h3 id="possible-objections">Possible Objections</h3>
  612. <p>I figure there’s likely to be some objections to the test, which are definitely worth talking through:</p>
  613. <blockquote>
  614. <p>I’d argue your use-case is probably more expensive than TodoMVC.</p>
  615. </blockquote>
  616. <ul>
  617. <li><strong>“TodoMVC isn’t idiomatic.”</strong> I checked this one out, and from what I understand every implementation <em>is</em> idiomatic, and framework experts file PRs against them when they aren’t.</li>
  618. <li><strong>“TodoMVC isn’t my use-case.”</strong> That’s probably true, but I’d argue your use-case is probably <em>more</em> expensive than TodoMVC. <a href="https://github.com/reddit/reddit-mobile/issues/247">Paul Irish recently did a performance audit on Reddit’s mobile site</a>, and he discovered, amongst other things, that 22 seconds were going into just booting up React’s components on mobile!</li>
  619. <li><strong>“A Nexus 5 / iPhone 5S isn’t what our users use.”</strong> That’s also probably true. I have the privilege of using good hardware, and I would imagine that many people don’t have access to top end phones, so I’d expect these numbers to be even worse in those cases.</li>
  620. <li><strong>“It’ll be better in the next version of &lt;insert framework here&gt;.”</strong> This may well be true, but it doesn’t do much for anything you’ve shipped with the current version.</li>
  621. </ul>
  622. <h2 id="the-64k-question">The $64k question</h2>
  623. <p>So, inevitably, the question comes: <em>“Should you use a framework?”</em></p>
  624. <p>I can’t answer that question, because I think it’s entirely your call. There are a million and one reasons why you may feel you need to use one. But, for what it’s worth, here are my thoughts:</p>
  625. <ul>
  626. <li><strong>Frameworks contribute ideas and concepts</strong>. Frameworks are a crucial part of understanding what approaches work, and what don’t, and that ultimately drives improvements at the platform level. In that regard I think they serve as important testing grounds for the platform changes of tomorrow and let us figure things out before they become embedded in the web permanently.</li>
  627. <li><strong>Frameworks are an inversion of control</strong>. The reason I parked libraries earlier on is that you can swap them out. Frameworks, on the other hand, invert control. <em>They</em> control the lifecycle of the app, and give <em>you</em> entry points where your code runs. You’re still responsible for the final code, but you’re not in control.</li>
  628. <li><strong>Frameworks are expensive on mobile</strong>. Compared to vanilla, at least. For me, prohibitively so, but everyone has their own limits of what they’re willing to tolerate.</li>
  629. </ul>
  630. <blockquote>
  631. <p>Investing in knowledge of the web platform itself is the best long-term bet.</p>
  632. </blockquote>
  633. <p>As I look at frameworks, I see the ergonomic benefits (and those are important, I agree!), but I can’t help but feel that, for many developers, <strong>investing in knowledge of the web platform itself is the best long-term bet</strong>. Frameworks come and go, it just seems to be the ebb and flow of the web, and, as I said above, they <em>do</em> contribute ideas and patterns. But if you ever find that the one you use no longer works for you, or has a bug that remains unfixed, being able to understand the platform that underpins it will help enormously.</p>
  634. <h2 id="the-bigger-conversation">The bigger conversation</h2>
  635. <p>When I wrote the post earlier in the year about React, I said this:</p>
  636. <blockquote class="quote">
  637. <p>It seems to me that developer ergonomics should be less important than our users’ needs.</p>
  638. </blockquote>
  639. <p>I still believe that. As much as I love the idea of having an easier life, I don’t want to ship something to people that doesn’t run well, and I don’t want them to pay the cost. Today I’m concerned by the costs of booting up frameworks on mobile.</p>
  640. <blockquote>
  641. <p>I’m concerned by the costs of booting up frameworks on mobile.</p>
  642. </blockquote>
  643. <p>This is only a first step. There are other metrics besides bootstrapping I’ve barely had chance to look into: memory usage, long-term CPU usage, and impact on frame rate to name three. Overall, I think we have more to do to properly assess the impact of code that we ship to our users and the costs we pass on to them.</p>
  644. <p>If we can achieve fast-booting, low-memory, smooth-executing frameworks with good ergonomics we’ll be onto a winner. Until then, for mobile at least, I’ll be sticking to the vanilla web platform.</p>
  645. </article>
  646. </section>
  647. <nav id="jumpto">
  648. <p>
  649. <a href="/david/blog/">Accueil du blog</a> |
  650. <a href="https://aerotwist.com/blog/the-cost-of-frameworks/">Source originale</a> |
  651. <a href="/david/stream/2019/">Accueil du flux</a>
  652. </p>
  653. </nav>
  654. <footer>
  655. <div>
  656. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  657. <p>
  658. Bonjour/Hi!
  659. Je suis <a href="/david/" title="Profil public">David&nbsp;Larlet</a>, je vis actuellement à Montréal et j’alimente cet espace depuis 15 ans. <br>
  660. Si tu as apprécié cette lecture, n’hésite pas à poursuivre ton exploration. Par exemple via les <a href="/david/blog/" title="Expériences bienveillantes">réflexions bimestrielles</a>, la <a href="/david/stream/2019/" title="Pensées (dés)articulées">veille hebdomadaire</a> ou en t’abonnant au <a href="/david/log/" title="S’abonner aux publications via RSS">flux RSS</a> (<a href="/david/blog/2019/flux-rss/" title="Tiens c’est quoi un flux RSS ?">so 2005</a>).
  661. </p>
  662. <p>
  663. Je m’intéresse à la place que je peux avoir dans ce monde. En tant qu’humain, en tant que membre d’une famille et en tant qu’associé d’une coopérative. De temps en temps, je fais aussi des <a href="https://github.com/davidbgk" title="Principalement sur Github mais aussi ailleurs">trucs techniques</a>. Et encore plus rarement, <a href="/david/talks/" title="En ce moment je laisse plutôt la place aux autres">j’en parle</a>.
  664. </p>
  665. <p>
  666. Voici quelques articles choisis :
  667. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  668. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  669. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  670. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  671. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  672. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  673. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  674. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  675. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  676. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  677. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  678. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  679. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  680. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  681. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  682. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  683. </p>
  684. <p>
  685. On peut <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">échanger par courriel</a>. Si éventuellement tu souhaites que l’on travaille ensemble, tu devrais commencer par consulter le <a href="http://larlet.com">profil dédié à mon activité professionnelle</a> et/ou contacter directement <a href="http://scopyleft.fr/">scopyleft</a>, la <abbr title="Société coopérative et participative">SCOP</abbr> dont je fais partie depuis six ans. Je recommande au préalable de lire <a href="/david/blog/2018/cout-site/" title="Attention ce qui va suivre peut vous choquer">combien coûte un site</a> et pourquoi je suis plutôt favorable à une <a href="/david/pro/devis/" title="Discutons-en !">non-demande de devis</a>.
  686. </p>
  687. <p>
  688. Je ne traque pas ta navigation mais mon
  689. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  690. conserve des logs d’accès.
  691. </p>
  692. </div>
  693. </footer>
  694. <script type="text/javascript">
  695. ;(_ => {
  696. const jumper = document.getElementById('jumper')
  697. jumper.addEventListener('click', e => {
  698. e.preventDefault()
  699. const anchor = e.target.getAttribute('href')
  700. const targetEl = document.getElementById(anchor.substring(1))
  701. targetEl.scrollIntoView({behavior: 'smooth'})
  702. })
  703. })()
  704. </script>