Where Do You Compute?
October 30th, 2020
If I had to summarize what I do day-to-day, I'd say I build websites. Leaving aside the actual nuances of what that work requires, I suspect most people would hear that and form a similar mental picture: some text, some images and maybe some dynamic elements that pop up or move around all inside a browser window.
Though that seems simple enough, what I've had trouble articulating lately is regardless of what that website looks like on your screen, how those elements you see on screen were pulled together is anything but simple. Communicating about my work, is perhaps the most important part of my job and I worry about my team's standing when I can't articulate that effort clearly.
So, better to write out my thoughts now, in the hopes they'll crystallize into something I can speak to later.
You'll have to bear with me, the industry hasn't fully settled here, so while my thinking is in flux, I've decided to illustrate this new wild web world using the concept of "computation". You don't need a fancy degree to follow along, simply think of computation as an analogue for "where the work happens."
(Nb: I will be leaving aside the endless possibilities of frameworks, programming languages, server stacks, virtual environments, containerization, etc. Trying to understand how HTML, CSS and Javascript come together to form the page you're currently browsing is a marvel of technology, too much for the scope of a single article.)
Server-side Computation (on Arrival)
Most websites up to this point, have relied on a single server (i.e., "someone else's computer", not yours) doing all of the computation to pull a web page together. It receives the request, pulls data from a database, injects that into a template, and displays that on your screen. If the user logs in, that interaction is handled by the same server that displays the webpage; different functionality all computed on the same server. Complicated, sure, but not impenetrable to most people. You might come across this referred to as an "integrated" or "monolithic" approach.
Client-side Computation (on Arrival)
These days, data is increasingly being kept in different places, be it a third party service, or different part of a database cluster, or some other strange arrangement. How then does that data end up on a web page? Most of the computation is still handled by the server (i.e., the place where your web page lives) but your visitor's browser can go ahead and fetch additional data when they arrive via a Javascript request.
See what happened there? Where previously you needed a powerful server to do all the heavy lifting, now you've split the computation among the server, the visitor's computer and browser (also referred to as a "Client"), and a third party server. The split of the computation depends on the architecture of the web page, many rely heavily on the server for computation because it is likely the most powerful, but some developers opt to minimize server work and put more computation onto the Client. (The downside to the latter is there are no guarantees that a visitor's browser is capable of enough computation to deliver a good experience: think older phones, lower tier models, or spotty connections.)
Computation Before Arrival
I did mention there was another way to split that computational whole: you could fetch all of the data (the server templates and third party data) before a visitor even reaches your site. Now, a tiny amount of compute is done at our third party's server, then almost all of the compute happens on a developer's machine or a "build server". All of the work that would have been done by the original server and the browser, is squeezed together before the visitor arrives at your site.
Why? Well, this way we save requests going out across the network to fetch new data and we save the Client from having to do much work. The pay off for doing all the compute ahead of time is speed. Simply put, you can't trust everyone to have a fast network, a fast browser and certainly not both together all of the time. The page works exactly like one I would have put together 15 years ago by hand: static and beautiful HTML, CSS and Javascript. You may come across this growing movement as the "JAMStack".
Drawbacks? Well, remember that example about a user logging in from before? Pretty hard to do when you don't have any access to a database to look up whether a user exists or that their password matches. Remember this page is already built, it has no ability to include logic or dynamic functions on its own. The limitations of putting all of the computation before the user visits in this second example versus computation happening before-and-during a user's visit is pretty stark: a static web page cannot have dynamic functions on its own, at least not without some clever hacks.
I'll warn you now, this is where things start to get mind-bending.
Computation at the Edge (Between Arrivals?)
Companies like AWS, Netlify and Cloudflare, on the forefront of this movement to split up computation have worked very hard to build massive distribution networks. In essence, scores of simple but oh-so-fast servers all over the planet. These servers aren't as complicated as the server setup to host your WordPress site, they really only do HTML and client-side assets, including Javascript. But, these servers live in every major city center across the world, so they're physically closer to your visitors and the economies of scale wielded by these larger companies mean that these machines are way more powerful and way better networked than anything you could achieve yourself.
So you serve a website that takes all of the computation and handles it at build time, well before your visitors can even see the site, and then spreads these pages far and wide across the distribution network. Dynamic functionality can be added on using javascript in the visitors browser, like before... though that brings back the original problem we meant to solve: slow networks and slow browsers. Uh-oh.[^initial]
[^initial]: Statically built web pages do make the first load of these pages quicker and ensure there's something for Google's search indexing algorithm to crawl over, so it's not as if there are zero benefits. If you have read to the end or are a close reader and have figured out my final conceit, do know that staging dynamic vs. static build processes as a binary was intentional for dramatic effect.
The geniuses behind these distribution networks thought of something very clever since they had all of this server power "lying around": what if they allowed developers to write simple and restricted functions (mostly in javascript, but just about any language will fly these days) that can be run not when the user lands on the page, but just as they signal where they mean to go? Now a person clicks on a link and before they finish the journey to the next page, computation can occur.
Let me make a bad visual/pop-cultural analogy to help illustrate: remember the infamous scene from the Matrix, where the camera "slows" down to show Neo dodging bullets? When a gun is fired, the bullet travels so fast it's intuitive to assume that it effectively hits its target instantaneously. What the slow motion reveals in the file is that there is plenty of time and space between to do work between the firing of a (cgi!) gun and it reaching its target... if you're as fast as Neo. Think of these edge servers as just that: more powerful and faster than you could conceive, enough to squeeze in an opportunity to perform a bit of computation immediately after your visitor has made their request and before that web page has returned. They're so fast they can add information, fetch data, spit out templates and so on to any page in single-digit-milliseconds!
If you're clever and have a specific use case dreamed up, you can have a reasonably durable website, that's wicked fast, has little to no operations cost (e.g., no load balancers, database caching, etc.) and should theoretically scale in a fashion that doesn't leave your IT team in cold sweats. I've seen examples of edge functions grab the visitor's rough location and use it to serve a statically built web page with localized to that language instead of the default. This is one example, imagine authorizing users and serving different content, splitting visitors for A/B testing, or something I have yet to imagine.
You Were Right, Building Websites Did Get Complicated
To recap, we have server-side computation on arrival, computation in the client (read: browser), computation ahead of time in the form of static sites pulling data from various APIs then building the page and now computation at the "edge".
A Skeptic wouldn't be far off asking how many types of websites or apps can have their computation broken up into chunks like this? Shouldn't the developer community stick to what's worked for 20 years? And honestly, the answer to both remains to be seen. Content-only websites and landing pages are a perfect fit, or an eCommerce site where content is mostly static and payment processing is handled by a third party, say Stripe, is another good use case. However, an app that processes large swaths of real-time data? No chance and anything between those use cases would be tough to provide a definitive answer.
Now try explaining to a non-developer that where my straw-man skeptic falls apart is the assumption that you would only choose one of these pathways, rather than choosing some/all of them depending upon the need.
Luckily for us, the user/client never really needs to know what it took to get their site or app to their screen. However, communicating about our work is a developer problem. Communicating how a site was constructed, what those advantages are (or what disadvantages these choices minimize) and how that provides value to the client or customer is incredibly important to successful work. Being a successful developer is hard enough writing code that meets standards, budget and time allotments, let alone when you have trouble enough speaking about what it is that your team delivered in the first place.