Slide 1

Slide 1 text

Feb. 1 2020, PWA Night CONFERENCE 2020 Kazunari Hara, CyberAgent Scalable 〜こえのブログ最新事例〜

Slide 2

Slide 2 text

is a web app focusing on UX

Slide 3

Slide 3 text

voice.ameba.jp voice.ameba.jp voice.ameba.jp AD Loading too slow Unexpected layout shift Desktop view on mobile

Slide 4

Slide 4 text

meow にゃーん (やーん)

Slide 5

Slide 5 text

● Reliable - Load instantly even when offline ● Fast - Respond quickly and no junk scrolling ● Engaging - Feel like a natural app on the device https://developers.google.com/web/progressive-web-apps

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

● Reliable - Load instantly even when offline ● Fast - Respond quickly and no junk scrolling ● Engaging - Feel like a natural app on the device ● Scalable - Being improved consistently

Slide 8

Slide 8 text

こえのブログ Posting a blog entry just by speaking ● Audio recording ● Speech-to-Text ● Audio player ● Installable ●

Slide 9

Slide 9 text

● Posting blog with audio and text ● The editor is a PWA

Slide 10

Slide 10 text

Device Microphone with MediaDevices.getUserMedia() https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia navigator.mediaDevices.getUserMedia({ audio: autoGainControl: false, channelCount: 1, echoCancellation: true, noiseSuppression: true, }) .then(stream => // use stream);

Slide 11

Slide 11 text

Getting Audio from Mic and save/post it later const mediaRecorder = new MediaRecorder(stream, options); mediaRecorder.addEventListener('stop', () => { const audioBlob = new Blob(recordedChunks); const audioUrl = URL.createObjectURL(audioBlob); downloadLink.href = audioUrl; downloadLink.download = 'audio.wav'; }); mediaRecorder.start();

Slide 12

Slide 12 text

Transcoding with WASM reduces audio sizes https://github.com/Kagami/vmsg ● In Web Worker ● Execute periodically ● WAV to MP3 -90% Audio sizes

Slide 13

Slide 13 text

Speech-to-Text on Cloud converts audio to text by applying powerful neural network models const speech = require('@google-cloud/speech'); const client = new speech.SpeechClient(); const recognize = () => { const request = { audio: { uri: 'gs://...' }, config: { languageCode: 'ja-JP', enableWordTimeOffsets: true }, }; return client.recognize(request); };

Slide 14

Slide 14 text

Saving Draft to IndexedDB and restore it

Slide 15

Slide 15 text

Playing Audio with the user’s permission ● Asking permission to play audio after prompt on the first encounter ● If the user says “OK”, start playing audio and save the status to the browser storage ● If the user says “NO”, always ask before playing audio

Slide 16

Slide 16 text

Add to Home Screen as an application on OSes Criteria: ● https ● Service Worker with fetch ● Web App Manifest etc...

Slide 17

Slide 17 text

Full experiences: Works on Limited experiences:

Slide 18

Slide 18 text

Made with

Slide 19

Slide 19 text

Challenges for Scalable PWA

Slide 20

Slide 20 text

Single Origin with HTTPS helps overcome many challenges in PWAs ● Service worker scopes ● New feature permissions ● Controllable caches ● Reading cookies https://web.dev/multi-origin-pwas/ https://voices.ameba.jp/*/ {api, assets, images, src} voice.ameba.jp

Slide 21

Slide 21 text

App Strategy at Ameba こえのブログ shipped as a PWA: ● To create experimental fetures ● To provide app to major OSes ● Not to increase native app sizes ● PWA already works on many browsers

Slide 22

Slide 22 text

Including Polyfill to provide modern functionality on older browsers Some polyfill options: Bundle JS files with polyfills like core-js Using polyfill services like polyfill.io Differential script loading is also recommended ECMAScript 2016+ compatibility table is almost all green, but we need to load polyfill for now https://kangax.github.io/compat-t able/

Slide 23

Slide 23 text

Perf is Critical for user experience in PWA ● High priority for better UX ● Users prefer fast apps ● Better for business metrics voice.ameba.jp -44% Client Rendering Time +57% Page Views https://developers.cyberagent.co.jp/blog/archives/636/

Slide 24

Slide 24 text

https://twitter.com/addyosmani/status/1194710153414594561 Speed Badges on Chrome (planning)

Slide 25

Slide 25 text

Consistently Fast even during traffic spikes (13x traffic spikes occurred) Caches ensure site stability: ● Server-side caches ● Client-side caches

Slide 26

Slide 26 text

CDN for server-side stability ● Long TTL (1 day by default) ● Event-driven cache purging ● Serving stale content 98% Cache Hit Ratio 99% Cache Coverage

Slide 27

Slide 27 text

Service Worker for client-side caching ● Pre-caching app assets ○ updated precisely with server file update ● Runtime assets caches (except partial audios) ● HTTP caches if the browser does not support SW 98% Cache Cavarage

Slide 28

Slide 28 text

Cache Overview to improve site speed and consistency Origin Server / DB CDN Browser Purge on data update 98% Cache Cavarage 98% Cache Hit Fetch on file change High cache hit/coverage reduces unnecessary network requests

Slide 29

Slide 29 text

https://speakerdeck.com/herablog/using-cdn-to-improve-web-performance

Slide 30

Slide 30 text

Perf Budget helps ensure good user experience and business metrics The team need to maintain ● Perf budget ● Performance monitoring ● Improve decision making in development flow

Slide 31

Slide 31 text

Lab data vs Field data Assessing app performance using both types of data Lab Controlled/Consistent Real-world/Wild Field Lighthouse DevTools SpeedCurve Chrome User Experience Report Firebase Performance Monitoring SpeedCurve

Slide 32

Slide 32 text

Setting Perf Budget 1. Measuring the app in lab 2. Comparing metrics to competitors with 20% rule 3. Setting perf budgets 4. Monitoring budgets on a daily or a deployment basis 5. Updating budgets (e.g. adding Field metrics)

Slide 33

Slide 33 text

Perf Budget Now Setting aggressive budgets to find out regressions early FCP on Fast 3G <= 1.5s TTI on Fast 3G <= 3s Entrypoint HTML <= 14KB App shell JS <= 120KB Chunk JS <= 20KB

Slide 34

Slide 34 text

Monitoring Perf Budget Daily basis: SpeedCurve, Lighthouse CI... (Lab) Firebase Perf, CrUX… (Field) Deployment basis: SpeedCurve (Lab) Before deployment: Bundle size on CI

Slide 35

Slide 35 text

Perf Budget in development flow Some steps before consuming perf budget: ● Reducing size if possible ● Finding reason for size increases ● Issuing it to refactor later Exceeded? Ship it! Can reduce size? Reduce size Issue reason Consume budget Refactor Ship it! No Yes No Yes

Slide 36

Slide 36 text

Apps need to be cleaned everyday like cats

Slide 37

Slide 37 text

Continuous Deployment for site realiability ● Deployment is fast, easy, safe, and frequent ● Rollbacks are also easy ● Master branch is deployed automatically (on a daily basis, planning) >= 1,000 Releases in 10 Month Push CLI Purge

Slide 38

Slide 38 text

Tech Stack better performance, higher security, cheaper, easier scaling, and better DX Similar approach to JAMStack: ● Entire Project on a CDN ● Everything Lives in Git ● Modern Build Tools ● Automated Builds ● Atomic Deploys ● Instant Cache Invalidation Cloud Functions Cloud Storage Cloud Functions Web App Assets Audios API POST/DELETE API GET 93% Endpoints Cacheable https://jamstack.org/

Slide 39

Slide 39 text

Writing Tests ensures continuous deployment and app quality ● Logic unit testing ● Visual regression testing (pages) Tests working in progress: ● Logic unit testing on brosers ● Server-side testing https://qiita.com/junkisai/items/016c567a9fbade08d65b

Slide 40

Slide 40 text

Traffic reffarals to increase visitors Social media Native Apps Search engines Open PWA from native apps Primary pages are on ameblo.jp not voice.ameba.jp

Slide 41

Slide 41 text

Differential Rendering for social media crowlers and browsers inc Googlebot Server-side meta rendering ${pageTitle} Client-side app rendering

Slide 42

Slide 42 text

Image CDN creates eyecatch images displayed in social media ● Inserting landmark ● Cropping, resizing ● Converting appropriate format /image.jpg? crop=1200:630&width=1200&overlay=share

Slide 43

Slide 43 text

Accessibility improves user experience ● Color contrast ● Machine readability ● Keyboard usability ● User preferences etc... voice.ameba.jp Only a subset of accessibility issues can be automatically detected

Slide 44

Slide 44 text

Accessibility Tools checks additional accessibility issues Checking screen reader using VoiceOver Inspecting Accessibility Tree using DevTools Collaboration between designers and developers using VisBug

Slide 45

Slide 45 text

Keyboard Usability screen reader usability as well Ensuring current position: ● Displaying outlines ● Controlling focuses

Slide 46

Slide 46 text

Reduce Motions stops animations if the user prefers-reduced-motion @media (prefers-reduced-motion: reduce) { .item-placeholder { animation: none; } }

Slide 47

Slide 47 text

Dark Mode respects prefers-color-scheme Applied with CSS custom properties @media (prefers-color-scheme: dark) { :root { --app-background-color: black; --app-text-color: white; } } Planning

Slide 48

Slide 48 text

Accessibility Guidelines explains how can we improve user experience ● Based on WCAG 2.1 ● Web Bundle is available https://openameba.github.io/a11y-guidelines/ openameba.github.io https://github.com/openameba/a11y-guidelines/releases

Slide 49

Slide 49 text

Progressive Enhancement to provide first-class experiences to the user Not only for visual designs: ● Feature detections ● Polyfill or other solutions voice.ameba.jp voice.ameba.jp

Slide 50

Slide 50 text

Permissions API Not available: Always asking the permission Available: Checking the permission before recording const checkMicPermission = async () => { const permissionStatus = await navigator.permissions.query({ name: 'microphone', }); if (permissionStatus.state === 'denied') { // Display dialog... } else { // Start recording... } // You can also use addEventListener // permissionStatus.addEventListener('change'); };

Slide 51

Slide 51 text

Native Lazy-loading defers the loading of off-screen elements ”” ””src=”https://...” if ('loading' in HTMLImageElement.prototype) { // copy value from data-src to src } else {} // fallback with Intersection Observer https://twitter.com/herablog/status/1164475719100952576

Slide 52

Slide 52 text

Is Native Lazy-loading too eager? If it is true for your app, you can load images with ● Intersection Observer ● Element Lazy-rendering https://calendar.perfplanet.com/2019/native-image-lazy-loading-in-chrome-is-way-too-eager/

Slide 53

Slide 53 text

Web Share invokes the native sharing mechanism of the device The apps or the person could be suggested based on the user’s engagement

Slide 54

Slide 54 text

Web Share invokes the native sharing mechanism of the device if ('share' in navigator) { // Chrome, Safari navigator.share({ title: '', text: '', url: '' }); } else { // Display custom dialog }

Slide 55

Slide 55 text

Wake Lock prevents devices from dimming while recording The users can ● check the remaining time ● tap the stop button ● remove noises of when the device starts recording in background O rigin Trial

Slide 56

Slide 56 text

Wake Lock prevents devices from dimming while recording if ('wakeLock' in navigator) { // Chrome let wakeLock; const requestWakeLock = async () => { try { wakeLock = await navigator.wakeLock.request('screen'); } catch (err) { console.error(err); } }; document.addEventListener('visibilitychange', handler); document.addEventListener('fullscreenchange', handler); } else { // you can use NoSleep.js if wake lock is a critical fearture // no fallback also makes sence } O rigin Trial

Slide 57

Slide 57 text

Native File System enables developers to control device storage Use cases (planning): ● opening files in the storage ● saving recorded audios ● downloading favorite audios Planning

Slide 58

Slide 58 text

Native File System enables developers to control device storage // Chrome if (‘chooseFileSystemEntries’ in window) { const fileHandle = await window.chooseFileSystemEntries(); } else { // fallback with HTML element // use to open file // use to download files } Planning

Slide 59

Slide 59 text

Periodic Background Sync refreshes data periodically in backgound Use cases (planning): ● updating rankings ● downloading audios by favorite bloggers Planning

Slide 60

Slide 60 text

Periodic Background Sync refreshes data periodically in backgound const registration = await navigator.serviceWorker.ready; if ('periodicSync' in registration) { // Chrome await registration.periodicSync.register( 'content-sync', { minInterval: 24 * 60 * 60 * 1000 } // one day ); } Planning

Slide 61

Slide 61 text

UA Client Hints simplify User Agent string HTTP request header , User-Agent, is going to be freezed Tested in Google Chrome Canary 81 if (req.http.Sec-CH-UA ~ "Google Chrome (\d+)$") if (std.atoi(re.group.1) >= 63) { call esm_origin; } }

Slide 62

Slide 62 text

Project Fugu for the future web apps https://goo.gle/fugu-api-tracker

Slide 63

Slide 63 text

Passion and Collaborations

Slide 64

Slide 64 text

@herablog Related documents: ● アメブロ2019: こえのブログでのPWA https://developers.cyberagent.co.jp/blog/archives/20506/ ● Web App Checklist 〜高品質のWebアプリケーションをつくる ために〜 https://speakerdeck.com/herablog/web-app-checklist-2019-at-inside-frontend ● こえのブログでのPWA ~ PWA編 ~ https://speakerdeck.com/herablog/pwa-night-vol-dot-4 ● こえのブログでのPWA ~ 開発現場編 ~ https://speakerdeck.com/herablog/koe-no-blog-pwa ● 最新CDN入門 WEB+DB PRESS Vol.109 https://gihyo.jp/magazine/wdpress/archive/2019/vol109 ● CDNフル活用でつくる高速 Webアプリ https://speakerdeck.com/herablog/using-cdn-to-improve-web-performance Life Have a nice