{"action":"created","comment":{"url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls/comments/209200144","pull_request_review_id":145164834,"id":209200144,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwOTIwMDE0NA==","diff_hunk":"@@ -0,0 +1,401 @@\n+---\n+template: post.html\n+title: \"From O(4G) to O(ffline) - Lessons learned from performance\"\n+description: >\n+    One of our applications was slow, so we decided to invest time in performance.\n+date: 2018-08-09\n+author: Even Stensberg\n+tags: FrontEnd, Performance\n+excerpt_separator: <!--more-->\n+---\n+\n+When shifting one of our applications towards Client Side Rendering from Server Side, performance became important. With Server Side Rendering, there was no thought around how the user percieved our application, as the browser wasn't rendering content on the fly, our server did.\n+\n+Since then, we have moved towards a Client Sided approach to make user experience better. The stack is now using ReactJS, Laravel and VueJS. The mixture between a Server Side Library and two Client Side Libraries, made it reasonable to think that the application had opportunities to improve.\n+ \n+When developing applications, many developers think that performance is a field of its own. It certainly is, and the major amount of the time a developer spends on performance isn't necessarily around making optimizations. That being said, some of the optimizations we had, was already in reach. Libaries themselves had embeded production settings.\n+\n+To build our entire application, we use a combination of webpack, (PHP) composer and gulp. We chose webpack because it is stable. The tool is useful for optimizing code and abstracting source code into smaller pieces of code (chunks). To build global stylesheets, webpack wasn't as useful as gulp is. In addition to these two, a part of our stack is written in [Laravel](https://laravel.com/), which would need to use our generated chunks and CSS.\n+\n+#### Performance budgets\n+\n+With no prior work on performance, we did not have a estimated budget. When starting to optimize your application, a estimated bundle size and a goal to work towards is important, not is it only motivational, but it gives an indication of how small the application needs to be in order to be fast. U.S Web design system has a [primer website](https://designsystem.digital.gov/performance/how/) to get you started with perf budgets that makes sense.\n+\n+#### Lighthouse, Performance Analyzer Tools\n+\n+[Lighthouse](https://developers.google.com/web/tools/lighthouse/) has helped the team to find out where performance optimizations was needed. It provided us insight on where and how to improve and has been a helpful tool for us. Another great resource is the opportunity to analyze generated javascript files, what is commonly refered to as bundles. By using [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer#webpack-bundle-analyzer) we found duplicate  code that we could have abstracted to chunks containing this code. \n+\n+We also used [webpagetest](https://webpagetest.org/) to measure speed index and general website stats in order to get an estimate of our performance budget. Snapshots of things like First Meaningful Paint and Time To First Byte became a valueable label for our measurements.\n+\n+#### Lazy and dynamically loading components and routes \n+\n+Route based loading made it easier for us to provide a user interface that was friendly with respect to performance. By showing a loading spinner while the route component was fetching, we were able to reduce amount of code shipped to the user. We made use of a library in React, [`react-lodable`](https://github.com/jamiebuilds/react-loadable).\n+\n+```js\n+\n+import React from 'react';\n+import ReactDOM from 'react-dom';\n+import Loadable from 'react-loadable';\n+\n+import FidgetSpinner from './spinner';\n+import Landing from './landing';\n+\n+const mountNode = document.getElementById('mypages');\n+\n+const LoadableComponent = Loadable({\n+\tloader: () => import('./auth'),\n+\tloading: FidgetSpinner\n+});\n+\n+ReactDOM.render(<LoadableComponent />, mountNode);\n+\n+```\n+\n+#### Managing non-JS frameworks with chunk splitting\n+\n+Making sure that a PHP library complies with a JavaScript wasn't as hard as one might think. The major blocker when using a PHP library with a JavaScript library was to make sure that performance wasn't suffering. A hybrid application, using server side rendering and client side rendering has the potential to scale well if done right. ","path":"src/posts/17-performance.md","position":56,"original_position":56,"commit_id":"4d40e9a28c1fb6c29cf15b5ee0680a3614e205f0","original_commit_id":"4d40e9a28c1fb6c29cf15b5ee0680a3614e205f0","user":{"login":"alexyork","id":259948,"node_id":"MDQ6VXNlcjI1OTk0OA==","avatar_url":"https://avatars2.githubusercontent.com/u/259948?v=4","gravatar_id":"","url":"https://api.github.com/users/alexyork","html_url":"https://github.com/alexyork","followers_url":"https://api.github.com/users/alexyork/followers","following_url":"https://api.github.com/users/alexyork/following{/other_user}","gists_url":"https://api.github.com/users/alexyork/gists{/gist_id}","starred_url":"https://api.github.com/users/alexyork/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexyork/subscriptions","organizations_url":"https://api.github.com/users/alexyork/orgs","repos_url":"https://api.github.com/users/alexyork/repos","events_url":"https://api.github.com/users/alexyork/events{/privacy}","received_events_url":"https://api.github.com/users/alexyork/received_events","type":"User","site_admin":false},"body":"Change \"complies with a JavaScript wasn't\" to \"complies with a JavaScript library wasn't\"","created_at":"2018-08-10T09:26:40Z","updated_at":"2018-08-10T09:30:13Z","html_url":"https://github.com/TeliaSoneraNorge/techblog/pull/1#discussion_r209200144","pull_request_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls/1","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls/comments/209200144"},"html":{"href":"https://github.com/TeliaSoneraNorge/techblog/pull/1#discussion_r209200144"},"pull_request":{"href":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls/1"}}},"pull_request":{"url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls/1","id":207282911,"node_id":"MDExOlB1bGxSZXF1ZXN0MjA3MjgyOTEx","html_url":"https://github.com/TeliaSoneraNorge/techblog/pull/1","diff_url":"https://github.com/TeliaSoneraNorge/techblog/pull/1.diff","patch_url":"https://github.com/TeliaSoneraNorge/techblog/pull/1.patch","issue_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/issues/1","number":1,"state":"open","locked":false,"title":"New Post on Performance","user":{"login":"ev1stensberg","id":16735925,"node_id":"MDQ6VXNlcjE2NzM1OTI1","avatar_url":"https://avatars0.githubusercontent.com/u/16735925?v=4","gravatar_id":"","url":"https://api.github.com/users/ev1stensberg","html_url":"https://github.com/ev1stensberg","followers_url":"https://api.github.com/users/ev1stensberg/followers","following_url":"https://api.github.com/users/ev1stensberg/following{/other_user}","gists_url":"https://api.github.com/users/ev1stensberg/gists{/gist_id}","starred_url":"https://api.github.com/users/ev1stensberg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ev1stensberg/subscriptions","organizations_url":"https://api.github.com/users/ev1stensberg/orgs","repos_url":"https://api.github.com/users/ev1stensberg/repos","events_url":"https://api.github.com/users/ev1stensberg/events{/privacy}","received_events_url":"https://api.github.com/users/ev1stensberg/received_events","type":"User","site_admin":false},"body":"A blog post about performance","created_at":"2018-08-09T11:20:19Z","updated_at":"2018-08-10T09:30:13Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7ad5734499c0688903199ad56f3c508b2ad6a5b4","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls/1/commits","review_comments_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls/1/comments","review_comment_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls/comments{/number}","comments_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/issues/1/comments","statuses_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/statuses/4d40e9a28c1fb6c29cf15b5ee0680a3614e205f0","head":{"label":"ev1stensberg:master","ref":"master","sha":"4d40e9a28c1fb6c29cf15b5ee0680a3614e205f0","user":{"login":"ev1stensberg","id":16735925,"node_id":"MDQ6VXNlcjE2NzM1OTI1","avatar_url":"https://avatars0.githubusercontent.com/u/16735925?v=4","gravatar_id":"","url":"https://api.github.com/users/ev1stensberg","html_url":"https://github.com/ev1stensberg","followers_url":"https://api.github.com/users/ev1stensberg/followers","following_url":"https://api.github.com/users/ev1stensberg/following{/other_user}","gists_url":"https://api.github.com/users/ev1stensberg/gists{/gist_id}","starred_url":"https://api.github.com/users/ev1stensberg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ev1stensberg/subscriptions","organizations_url":"https://api.github.com/users/ev1stensberg/orgs","repos_url":"https://api.github.com/users/ev1stensberg/repos","events_url":"https://api.github.com/users/ev1stensberg/events{/privacy}","received_events_url":"https://api.github.com/users/ev1stensberg/received_events","type":"User","site_admin":false},"repo":{"id":143259500,"node_id":"MDEwOlJlcG9zaXRvcnkxNDMyNTk1MDA=","name":"techblog","full_name":"ev1stensberg/techblog","owner":{"login":"ev1stensberg","id":16735925,"node_id":"MDQ6VXNlcjE2NzM1OTI1","avatar_url":"https://avatars0.githubusercontent.com/u/16735925?v=4","gravatar_id":"","url":"https://api.github.com/users/ev1stensberg","html_url":"https://github.com/ev1stensberg","followers_url":"https://api.github.com/users/ev1stensberg/followers","following_url":"https://api.github.com/users/ev1stensberg/following{/other_user}","gists_url":"https://api.github.com/users/ev1stensberg/gists{/gist_id}","starred_url":"https://api.github.com/users/ev1stensberg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ev1stensberg/subscriptions","organizations_url":"https://api.github.com/users/ev1stensberg/orgs","repos_url":"https://api.github.com/users/ev1stensberg/repos","events_url":"https://api.github.com/users/ev1stensberg/events{/privacy}","received_events_url":"https://api.github.com/users/ev1stensberg/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ev1stensberg/techblog","description":"The content of http://techblog.telia.no, the technically oriented company blog","fork":true,"url":"https://api.github.com/repos/ev1stensberg/techblog","forks_url":"https://api.github.com/repos/ev1stensberg/techblog/forks","keys_url":"https://api.github.com/repos/ev1stensberg/techblog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ev1stensberg/techblog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ev1stensberg/techblog/teams","hooks_url":"https://api.github.com/repos/ev1stensberg/techblog/hooks","issue_events_url":"https://api.github.com/repos/ev1stensberg/techblog/issues/events{/number}","events_url":"https://api.github.com/repos/ev1stensberg/techblog/events","assignees_url":"https://api.github.com/repos/ev1stensberg/techblog/assignees{/user}","branches_url":"https://api.github.com/repos/ev1stensberg/techblog/branches{/branch}","tags_url":"https://api.github.com/repos/ev1stensberg/techblog/tags","blobs_url":"https://api.github.com/repos/ev1stensberg/techblog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ev1stensberg/techblog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ev1stensberg/techblog/git/refs{/sha}","trees_url":"https://api.github.com/repos/ev1stensberg/techblog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ev1stensberg/techblog/statuses/{sha}","languages_url":"https://api.github.com/repos/ev1stensberg/techblog/languages","stargazers_url":"https://api.github.com/repos/ev1stensberg/techblog/stargazers","contributors_url":"https://api.github.com/repos/ev1stensberg/techblog/contributors","subscribers_url":"https://api.github.com/repos/ev1stensberg/techblog/subscribers","subscription_url":"https://api.github.com/repos/ev1stensberg/techblog/subscription","commits_url":"https://api.github.com/repos/ev1stensberg/techblog/commits{/sha}","git_commits_url":"https://api.github.com/repos/ev1stensberg/techblog/git/commits{/sha}","comments_url":"https://api.github.com/repos/ev1stensberg/techblog/comments{/number}","issue_comment_url":"https://api.github.com/repos/ev1stensberg/techblog/issues/comments{/number}","contents_url":"https://api.github.com/repos/ev1stensberg/techblog/contents/{+path}","compare_url":"https://api.github.com/repos/ev1stensberg/techblog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ev1stensberg/techblog/merges","archive_url":"https://api.github.com/repos/ev1stensberg/techblog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ev1stensberg/techblog/downloads","issues_url":"https://api.github.com/repos/ev1stensberg/techblog/issues{/number}","pulls_url":"https://api.github.com/repos/ev1stensberg/techblog/pulls{/number}","milestones_url":"https://api.github.com/repos/ev1stensberg/techblog/milestones{/number}","notifications_url":"https://api.github.com/repos/ev1stensberg/techblog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ev1stensberg/techblog/labels{/name}","releases_url":"https://api.github.com/repos/ev1stensberg/techblog/releases{/id}","deployments_url":"https://api.github.com/repos/ev1stensberg/techblog/deployments","created_at":"2018-08-02T07:36:31Z","updated_at":"2018-08-10T06:46:57Z","pushed_at":"2018-08-10T06:46:55Z","git_url":"git://github.com/ev1stensberg/techblog.git","ssh_url":"git@github.com:ev1stensberg/techblog.git","clone_url":"https://github.com/ev1stensberg/techblog.git","svn_url":"https://github.com/ev1stensberg/techblog","homepage":null,"size":984,"stargazers_count":0,"watchers_count":0,"language":"CSS","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"TeliaSoneraNorge:master","ref":"master","sha":"50353bf3f128e61f12a4091b6ff9351cf2a6c558","user":{"login":"TeliaSoneraNorge","id":17065465,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE3MDY1NDY1","avatar_url":"https://avatars0.githubusercontent.com/u/17065465?v=4","gravatar_id":"","url":"https://api.github.com/users/TeliaSoneraNorge","html_url":"https://github.com/TeliaSoneraNorge","followers_url":"https://api.github.com/users/TeliaSoneraNorge/followers","following_url":"https://api.github.com/users/TeliaSoneraNorge/following{/other_user}","gists_url":"https://api.github.com/users/TeliaSoneraNorge/gists{/gist_id}","starred_url":"https://api.github.com/users/TeliaSoneraNorge/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TeliaSoneraNorge/subscriptions","organizations_url":"https://api.github.com/users/TeliaSoneraNorge/orgs","repos_url":"https://api.github.com/users/TeliaSoneraNorge/repos","events_url":"https://api.github.com/users/TeliaSoneraNorge/events{/privacy}","received_events_url":"https://api.github.com/users/TeliaSoneraNorge/received_events","type":"Organization","site_admin":false},"repo":{"id":66065445,"node_id":"MDEwOlJlcG9zaXRvcnk2NjA2NTQ0NQ==","name":"techblog","full_name":"TeliaSoneraNorge/techblog","owner":{"login":"TeliaSoneraNorge","id":17065465,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE3MDY1NDY1","avatar_url":"https://avatars0.githubusercontent.com/u/17065465?v=4","gravatar_id":"","url":"https://api.github.com/users/TeliaSoneraNorge","html_url":"https://github.com/TeliaSoneraNorge","followers_url":"https://api.github.com/users/TeliaSoneraNorge/followers","following_url":"https://api.github.com/users/TeliaSoneraNorge/following{/other_user}","gists_url":"https://api.github.com/users/TeliaSoneraNorge/gists{/gist_id}","starred_url":"https://api.github.com/users/TeliaSoneraNorge/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TeliaSoneraNorge/subscriptions","organizations_url":"https://api.github.com/users/TeliaSoneraNorge/orgs","repos_url":"https://api.github.com/users/TeliaSoneraNorge/repos","events_url":"https://api.github.com/users/TeliaSoneraNorge/events{/privacy}","received_events_url":"https://api.github.com/users/TeliaSoneraNorge/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/TeliaSoneraNorge/techblog","description":"The content of http://techblog.telia.no, the technically oriented company blog","fork":false,"url":"https://api.github.com/repos/TeliaSoneraNorge/techblog","forks_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/forks","keys_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/teams","hooks_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/hooks","issue_events_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/issues/events{/number}","events_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/events","assignees_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/assignees{/user}","branches_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/branches{/branch}","tags_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/tags","blobs_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/git/refs{/sha}","trees_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/statuses/{sha}","languages_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/languages","stargazers_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/stargazers","contributors_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/contributors","subscribers_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/subscribers","subscription_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/subscription","commits_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/commits{/sha}","git_commits_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/git/commits{/sha}","comments_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/comments{/number}","issue_comment_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/issues/comments{/number}","contents_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/contents/{+path}","compare_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/merges","archive_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/downloads","issues_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/issues{/number}","pulls_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls{/number}","milestones_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/milestones{/number}","notifications_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/labels{/name}","releases_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/releases{/id}","deployments_url":"https://api.github.com/repos/TeliaSoneraNorge/techblog/deployments","created_at":"2016-08-19T08:15:19Z","updated_at":"2018-03-15T13:31:51Z","pushed_at":"2018-08-10T06:46:57Z","git_url":"git://github.com/TeliaSoneraNorge/techblog.git","ssh_url":"git@github.com:TeliaSoneraNorge/techblog.git","clone_url":"https://github.com/TeliaSoneraNorge/techblog.git","svn_url":"https://github.com/TeliaSoneraNorge/techblog","homepage":null,"size":928,"stargazers_count":0,"watchers_count":0,"language":"CSS","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1,"mirror_url":null,"archived":false,"open_issues_count":1,"license":null,"forks":1,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls/1"},"html":{"href":"https://github.com/TeliaSoneraNorge/techblog/pull/1"},"issue":{"href":"https://api.github.com/repos/TeliaSoneraNorge/techblog/issues/1"},"comments":{"href":"https://api.github.com/repos/TeliaSoneraNorge/techblog/issues/1/comments"},"review_comments":{"href":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls/1/comments"},"review_comment":{"href":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/TeliaSoneraNorge/techblog/pulls/1/commits"},"statuses":{"href":"https://api.github.com/repos/TeliaSoneraNorge/techblog/statuses/4d40e9a28c1fb6c29cf15b5ee0680a3614e205f0"}},"author_association":"NONE"}}
{"actor":{"display_login":"alexyork"}}