Let's explore architecture complexity, when abstractions are bad, how React Hooks work, and the importance of SvelteJS

4 Conference talks that changed my perspective as a web dev

8 minute readโ€ขLast updated

I've resolved to make 2021 my biggest year of learning yet. My blogging game has been pretty sporadic up until now... but I've finally decided to go all-in on the learning in public mantra with 1 post a week!

I used to believe blog posts had to be shining nuggets of wisdom only I could find. But with all the notes I take on a weekly basis, why not share my journal with the world too? ๐Ÿ˜

So, here's some of the most impactful conference talks I've found in the last year or so. I hope to check-in with more entries in the future, and I'd love to hear your own favorite talks in the comments!

๐ŸŽฅ View the talk + transcript here

If you've ever heard the someone say that something is "easy, but not simple," this is probably the talk they're referencing. This is an easy recommend to programmers in general (not just web devs). That said, I think this talk is especially relevant to frontend-ers nowadays with all the tools at our disposal.

It feels like web frameworks and "best practices" are moving towards some powerful new opinions:

  1. Everything is a component
  2. Functional programming is king
  3. State management is complex and deserves a rethink (hello state machines ๐Ÿ‘‹)

โ˜๏ธ These are the points Rich was getting at with this talk a decade ago! This is why I've resisted this talk multiple times throughout my web dev journey. As a junior getting comfortable with enterprise-grade React apps, it's helped me understand the why behind my team's architectural decisions.

  • Simple is an objective measure, no matter the person, reflecting how many interwoven pieces (complexity) there are in a given system

  • Easy is relative to every individual, reflecting how "familiar" or "near at hand" something feels

  • Agile programming encourages us to move fast without taking a step back

    • Complexity and tech debt pile up when we ignore signs of bad abstraction
    • Favorite quote of the talk: "But programmers fire the starting pistol every 100 yards and call it a (new) sprint!"
  • Replace complecting (knotted-up code with lots of interdependent pieces) with composing (modularity a la modern frontend frameworks)

    • Think building a castle from Legos instead of a "knitted castle" from interweaving
    • Separate horizontally, stratify vertically
  • Concrete improvements to make

    • "Stateful" variables complect values with change overtime; make transition from one state to another predictable (see state machines
    • Stay close to what the system does; consider the behavior over implementation details
    • Work with rules over conditional / switch case spaghetti
      • Given some data X, here's some rules to make it become Y
      • Lean on pure functions, which give you a consistent output for a given input

๐ŸŽฅ View the talk + transcript here

Here's another architecture-y talk that also reaches far beyond web development. If you're unfamiliar with Dan Abramov, he's one of the most prolific members of the React core team from a teaching standpoint alone. So if you want advice on architecting your web apps, this is your guy ๐Ÿ˜

This talk goes hand-in-hand with Kent C Dodd's post on "AHA programming". Generally, they're both addressing the biggest pitfall of component-based thinking: copy / paste feels like bad practice, so you abstract every piece of logic to its own little file.

Sure there's a place for abstraction, but there's also a place for duplication! This talk has a lot of examples and funny quotables to keep things light; definitely worth the watch.

  • If left unchecked, abstractions can become Frankenstein code overtime
    • An abstraction almost fits are use case, but not quite ๐Ÿ‘‰ we wittle away that round hole to fit our square peg
    • When bugs arise for one use case, we introduce fixes affecting tons of other use cases
  • ๐Ÿ‘ When abstraction is good
    • Makes your code more declarative / focus on a specific intent (see that Rich Hickey talk above ๐Ÿ˜‰)
    • Avoids missed bug fixes ๐Ÿ‘‰ fix it once, fix everywhere
  • ๐Ÿ‘Ž When abstraction is bad
    • Creates coupling - when it doesn't quite fit, you can create a monster of refactors
    • Adds indirection - creates layers and layers overtime; "We avoid spaghetti code, but we create lasagna code" ๐Ÿ‡ฎ๐Ÿ‡น
  • Ways to improve going forward
    • Test code that uses an abstraction, not the abstraction itself
      • If you remove that abstraction later, your tests explode!
      • Abstractions are just another implementation detail (again, TDD is king)
    • Don't commit to abstraction layers until you need them; "If a girl is into the same obscure bands as you are... that doesn't mean you're meant to be together"
    • Be ready to remove abstractions later; Be the one asking "Please inline this abstraction!" in a PR review!

In my opinion, this is the biggest bombshell to drop since React was first revealed ๐Ÿ’ฃ

A trigger warning is probably in order here: if you're a diehard React follower, this talk questions many practice React holds dear (including the virtual DOM itself!).

But even if you disagree with Rich's points, this talk is a serious landmark in the web framework canon. It also exposes what "bundlers," "compilers," and "reacting to change" all really mean under the hood. If you aren't convert to a Svelte fan after this, you'll at least understand where the web has been, and where it might be heading!

  • What is reactive programming?
    • It all started with spreadsheets
      • I change a value in one cell, and other cells "react" to those changes with formulas
      • Earliest example of only re-rendering values that change
    • It's 1) about tracking values and 2) updating dependents on that value
  • Problem with React's model
    • When state changes in a component, that whole component re-evaluates itself from the top
    • Treats your HTML like a black box; apply the change, then diff against the previous chunk
    • Really, React doesn't know about your "state values" or how they affect the DOM!
    • Bad signs for efficiency: I shouldn't need useMemo, useCallback, shouldComponentUpdate, etc
  • Instead of opting out of reevaluating state (a la useMemo), we could opt in by flagging state variables that depend on other state variables
    • Much like a spreadsheet; write formulas that flag which variables affect a given value
    • Svelte uses a custom $: operator to "flag" state that is computed from other state (example here)
  • Svelte is a compiler, not a runtime
    • In other words, a "Svelte" component compiles to JS your browser understand
    • No "runtime" (like React's virtual DOM) needs to get imported
    • Also means Svelte can bend the JS language to its will
      • Evan You, creator of VueJS - Svelte is too magical, since it lets you write JavaScript that isn't totally valid
      • Rich Harris' response - this opinion is like believing HTML, CSS and JS should live in separate files. CSS-in-JS is weird too, so what's wrong with this?
  • Some other cool demos of Svelte

This is a fast-paced and code-heavy talk, so you'll probably want 1x speed on this one.

That said... this is the most enlightening talk I've seen on React. Period. It's only 30 minutes long, but it gave me holistic understanding on how React hooks, state management, and re-rendering all work together. It also shows some huge use cases for "closure" in JS. If you have a web dev interview coming up, point to this! ๐Ÿ˜‰

Hard to write a succinct, bulleted list for this one. So, I just annotated the finished product to explain how everything works. Fair warning: it's a lot to take in!

๐Ÿš€ Functioning codepen to see it in action

const React = (function () {
  // create an array for all the state variables in our "React app"
  let stateValues = [];
  // start our state index at 0. We'll use this
  // to throw state into that array โ˜๏ธ everytime someone calls "useState"
  let index = 0;
  function useState(initValue) {
    // state should be set to either:
    // 1. the initial value (aka React.useState(initValue))
    // if this is the first time our component rendered
    // 2. the value from the *last* render
    // if we're re-rendering our component (aka stateValues[index])
    const state = stateValues[index] || initValue;
    // "freeze" our index to this particular useState call with _index.
    // prevents the index from changing before we try to call setState later!
    const _index = index;
    const setState = (newValue) => {
      stateValues[_index] = newValue;
    };
    // increment index so our next useState call doesn't override the state
    // we just stored above
    index++;
    return [state, setState];
  }
  function render(Component) {
    // every time we re-render our app,
    // update all our state variables starting from the top
    index = 0;
    const C = Component();
    // "render" the component (which calls the useState function)
    C.render();
    return C;
  }
  // return all our functions from this foe React "module"
  return { useState, render };
})();

function Component() {
  const [count, setCount] = React.useState(2);
  return {
    // whenever we "render" this component with React.render, 
    // just log the value of our state variable
    render: () => console.log({ count }),
    click: () => setCount(count + 1)
  };
}

let App = React.render(Component) // logs { count: 2 }
App.click() // sets the state at stateValues[0] to 3
App.click() // sets the state at stateValues[0] to 4
React.render(Component) // logs { count: 4 }