Curated insights on engineering design and performance. Selected from community blogs and tweets.
Vue.js and Angular
A simple way to be a better engineer, enjoy perf benefits, and move faster, is to not over-engineer your code. So many times I see projects suffer is from over-engineering. Really. That's usually the root cause. We need to stop assuming that complexity is tied to intelligence. Smart people keep things as simple as is legible.
If you find pride in your craft, it is tempting to pursue cleanliness in code. Do it for a while. But don't stop there. Don't be a clean code zealot. Clean code is not a goal. It's an attempt to make some sense out of the immense complexity of systems we're dealing with. Let clean code guide you. Then let it go.
If your first interaction is visual, such as reading an article or looking at an image, serve HTML for those things. Most frameworks now offer some sort of server-rendering feature – just ensure you're not serving up a load of buttons that don't work while the client-side JS is loading. You can lazily-load and execute code for discrete interactions, loading them in the order the user is most-likely to need them.
Code should be written for humans (including your future self), not the computer. Almost every performance optimization is a trade-off between speed and something else. In most cases, you give up readability, expressiveness and/or idiomaticism. These meta-values won't show up in your measurements, but that doesn't mean that they can be ignored.
Engineer experience should be in service of user experience. The end goal of our development is all about the people using our website. As we think about the UX challenges on our site, we can adapt the experience to guide engineers to do the right thing by default. We should deliver only the resources we need, and we should strive to have them arrive right before we need them.
Keep code simple. Make it easy to read and maintain by others. Design patterns are great, but use them when it’s clear they add value. Break everything down into simple ideas. Keep code clear, concise and to the point. Your team will appreciate the clarity this can add.
Shubhie Panicker Miller
One thing that is lost in the current SPA debate: definition of "modern SPA". Old school SPA: loads the entire app, before any rendering or interactivity. Modern SPA: SSR (or serve static pages) for fast rendering (FCP), min route level code splitting for faster interactivity.
React and Vercel
My philosophy on abstractions (like React components): If your abstraction works in 9 out of 10 cases. That's a good abstraction. If it's insufficient in one of those cases, then copy/paste (i.e. decompose into its pieces) and tweak it for that case. Don't change the abstraction.
When it comes down to it, shipping an architecture that requires less code to do something is the type of long-term benefit your future self (or coworkers) will thank you for. It's possible — likely, even — that adopting a model like this requires more up-front design thinking. There are far few batteries-included options available for decomposing apps into independently deliverable widgets.
In "thick" client apps, product code (everything else) takes up the majority of the client bundle size. There's a real opp here to move that into Server Components which can reduce that footprint significantly. As an example, consider the case of deeply wrapped components that ultimately render to a single div. Server Components could help remove that abstraction tax.
If you feel like you have good reason to believe a best practice or technique is not a good fit for your app, then you should trust your instincts and move forward with your solution. Sometimes a technique or best practice that might work well in many contexts can actually be an anti-pattern given another context.
Performance is a multi-faceted thing. It would be great if we could reduce it down to a single metric such as bundle size, but if you really want to cover all the bases, there are a lot of different angles to consider. These can include runtime CPU costs, power consumption and memory.
Fred K Schott
Astro / Snowpack
When you're working on a new project, you rarely know what code will be important long-term and what code is about to be deleted. I've thrown away enough code in my career to have learned that there's sometimes value in fast, messy coding. When you're starting a new project, it's okay to be a bit messy.
The solutions that engineers at (large companies) come up with aren't for the vast majority of software shops: they're often best for big companies that can afford to set a high engineering bar, that can afford large infrastructure teams and ops teams. There's a huge gap between what developer-influencers are writing about, versus the daily reality of most developers.
UI is a composition of components (Angular, React, etc), with composite and leaf components. Similarly, in the file system we have directories and files. Since the component tree and the file system have the same structure we can apply the same algorithms on top of them.
Front End tech disagreements can many times be summed up as unwillingness to see items from different perspectives. CSS scoping is different for documents versus apps. JS complexity is sometimes necessitated by business requirements. It's never "just" one answer.
There are nuanced but common trade-offs that make it totally reasonably to be right with both choices (in the SPA vs. MPA debate) depending on the situation. Like any other engineering decision. Write down your goals and non-goals and then analyze the trade-offs of the options and pick the one that seems to achieve more goals with fewer trade-offs.
Facilite local reasoning. You should be able to worry about each part of your code in isolation, without holding the entire system in your head. In my experience, this is the key to making complex systems scale, especially (but not only) in a large organization.
Different app types can lead to different LCP outcomes. SSG (using getStaticProps() to fetch your data and prerender your app at build time), SSR (using getServerSideProps() to fetch your data and prerender your app at request time), CSR (application shell on server, then fetch your data on the client side and re-render) and ISR (which is where you can build your site statically for some subset of pages -- and render the rest of the pages on-demand).
It takes quite some experience to write boring but easy to understand code. I don't think you should feel unqualified to write software just because you didn't go through rigorous CS training, but I also don't think you should ignore them. I took a pragmatic approach where I did a lot of things the dumb way first, which helps to reveal what I needed to learn to make it better.
If we use tools that let us express the same ideas with less code, just like jQuery did, our apps will be more robust. I've spent my career at the intersection of journalism and software, and I've come to believe that writing code has more in common with writing prose than it does with engineering.
I've been using Eleventy a lot recently and loving it because it so nicely handles the fully static extreme, while web components handle the dynamic side and trivially integrate with eleventy because they're just HTML elements. It's a really great dev and user experience.
Begin with a "static first" philosophy when building a Jamstack site. Use deferred rendering when you have to render a lot of pages. Use SSR judiciously whenever the content cannot be statically rendered. Use edge rendering when you need to modify pages that have already been rendered.
How many web developers, to this day, don't test on a phone locally when building apps? This isn't just about small screens, we need to assume that we're building for weaker, slower computers. I think we need to bake mobile into our dev workflow, not just as some final pre-launch check.
David K Piano
To beginner developers, "anti-pattern" means "never do this" when in reality, it should mean "evaluate if/when you should do this and understand the caveats"
Performance budgets keep everyone on the same page. They help to create a culture of shared enthusiasm for improving the lived user experience. Teams with budgets also find it easier to track and graph progress. This helps support executive sponsors who then have meaningful metrics to point to in justifying the investments being made.
Next.js and Vercel
As (React) web apps became more complex, new solutions emerged to more easily share logic between components. Redux quickly grew to the most popular state management solution. React Context gave us a first-party solution to share logic between components. This solved UI state for many cases. Eventually we will have `useSelectedContext`.
I recommend that Components should only consume custom hooks, no primitive hooks. Encapsulate your concerns and convey the context correctly via custom hooks.
You should not be using Context as a global state. You should be using a state machine if it's needed, but moving your state DOWN will generally solve any "too many re-render" issues you are having.
Stop treating your code like some precious gem that you're handing down you future generations. They don't give a shit about your abstractions, your DRY, or your brilliant design.Legacy code becomes legacy by doing its job well enough that no one wants to touch it.
Here's my take on UI as pure functions vs. embracing the mutability of the DOM...No one gives a shit. People use what's easy to adopt and makes them feel productive. Everything else is a mythology you build up around your project to make its success seem more profound than that.