$30 off During Our Annual Pro Sale. View Details »

Managing "side effect" in Frontend Development

Managing "side effect" in Frontend Development

In the web frontend development context, we should care about function's side effect not only for simplicity of the function's responsibility, but also for preventing unnecessary/dangerous code from leaking into client side.

This slides show why we should care about side effect in frontend development, and introduce treeche, which is the tool to detect side effect in the ESModule

Shinobu Hayashi

November 17, 2023
Tweet

More Decks by Shinobu Hayashi

Other Decks in Technology

Transcript

  1. $ who am i Web Developer at Nikkei GitHub: @Shinyaigeek

    Twitter: @Shinyaigeek Love: Web, OSS, Beer, Cooking, others... Member of pnpm Shinobu Hayashi/ 林 仁 ` ` ` `
  2. $ What is "side effect" ? > In computer science,

    an operation, function or expression is said to have a side effect if it modifies some state variable value(s) outside its local environment, which is to say if it has any observable effect other than its primary effect of returning a value to the invoker of the operation from: wikipedia expression: file-system I/O, stdio, etc... function: an effect to outside of the function's scope ✅ This talk is aimed on a function’s "side effect" in Frontend context
  3. $ Example of a function’s side effects readArticle function have

    an effect on counter variable which is located in outside of readArticle let counter = 0; const readArticle = function (id) { counter++ // ❗️ return `${some nice article accord with id}`; } ` ` ` ` ` `
  4. $ Why Should We Care About Side Effect In Frontend

    Development? To keep function simple, of course To eliminate unnecessary code from built output JavaScript ✅ This talk is aimed to built JavaScript
  5. $ Why Eliminate Unnecessary Code from Built JavaScript? lead to

    less bundle size, more UX! prevent danger code from leaking internal code for developing environment-specific code (like server, browser)
  6. $ What is Tree Shaking? A. The function to eliminate

    unused modules from bundle. Bundler will eliminate getCurrentDay from bundle because it is not used and have no effect on bundled code // module.ts export getCurrentMonth = (date: Date = new Date()) => { return date.getMonth(); } export getCurrentDay = (date: Date = new Date()) => { return date.getDay(); } // app.ts import { getCurrentMonth } from './module.ts'; someNiceFunction(getCurrentMonth()); ` `
  7. $The Impact of Side Effects in Frontend Development For example,

    what output bundler will generate from the above input… Along with ECMAScript’s specification, ESModule should be evaluated overall when it is imported from others. // config.ts const defineConfig = (config) => { return { version: 1, ...config } } export const configA = defineConfig({ name: "A" }) export const configB = defineConfig({ name: "B" }) export const configForTest = defineConfig({ name: "Testing", debug: "some scare secret" }) // app.ts import { configB } from "./config" console.log(configB)
  8. $ How to accomplish better tree-shaking Bundler "Because I don’t

    know whether defineConfig function have side effect or not…" Output: const defineConfig = (config) => { return { version: 1, ...config } }; defineConfig({ name: "A" }); // Why ❗️❓ app does not use this ❗️ const configB = defineConfig({ name: "B" }); defineConfig({ name: "Testing", debug: "some scare secret" }); // Why ❗️❓ app does not use this ❗️ console.log(configB); ` `
  9. $ Eliminating ESModule’s Side Effects from the Bundle Bundler "Because

    I don’t know whether defineConfig function have side effect or not…" defineConfig in the example is pure, but perhaps defineConfig may have side effect like below : Human can recognize defineConfig is pure easily, but Bundler cannot recognize defineConfig is pure or with side effect ` ` ` ` ` ` let version = 0; const defineConfig = (config) => { version += 1; return { version, ...config } }; ` ` ` `
  10. $ ESModule’s Side Effect with Module Bundler Module Bundler will

    bundle also side effect Module Bundler cannot find the facing module's exported member is pure or not
  11. Eliminating ESModule’s Side Effects from the Bundle example with terser:

    terser --compress pure_funcs=[defineConfig] We can eliminate ESModule’s side effect from bundle by specializing defineConfig as a pure function ` ` ` ` const defineConfig = (config) => { return { version: 1, ...config } }; defineConfig({ name: "A" }); // 👈 Terser can remove this because defineConfig is pure and this invoke does not const configB = defineConfig({ name: "B" }); defineConfig({ name: "Testing", debug: "some scare secret" }); // 👈 Terser can remove this because defineConfi console.log(configB);
  12. $ How to accomplish better tree-shaking We can handle this

    with the implicit pure functions specification but we cannot detect this easily...
  13. $ Treeche, Tree-Shakable Checker Treeche will throw alert for the

    input such the former example Treeche is the tool to detect the code which is not tree-shakable in ESModules. 🚨 /~~~/config.ts is not tree-shakable due to the following code: defineConfig({ ^^^^^^^^^^^^^^ name: "A" ^^^^^^^^^^^ }); ^^^ defineConfig({ ^^^^^^^^^^^^^^ name: "Testing", debug: "some scare secret" ^^^^^^^^^^^ }); ^^^ Creator of Treeche ✌️ 👉
  14. $ How Treeche works A. Treeche bundles each input modules

    internally on memory. Treeche generate JavaScript code like above, and bundle this with rollup by resolving memory prefixed module path as specified input modules. And then, Treeche checks the built output and throw a pretty alert if there are the code other than ImportSpecifier because it indicates this modules is not tree-shakable. Feature Native TypeScript support for input Checking the multiple files with Node.js glob import * from "memory:specified modules"; ` ` ` `
  15. $ Summary Frontend devs should care about ESModule's side effect

    to reduce bundle size to prevent some dangerous code from leaking treeche is useful to detect side effects in modules