Why Write Boring Apps?
If you’ve built any kind of web-based application in the last decade, chances are you were building a single-page application (SPA). This means you’ve been writing JavaScript or Typescript, had to work with the Node Package Manager or Yarn, needed to understand a framework like Angular, React or Vue and had to learn a new testing framework (or two). And that would have been only the basics. You would probably use other libraries, frameworks and toolkits: for styling the user interface, for doing API calls using REST and/or GraphQL, and for writing reusable components. But we usually take the pain, because the alternative is just… “boring”.
But it is time to reflect on this approach. It drags in a lot of complexity, but what does it give us? More complexity! At the end of the day, everyone will probably agree that simplicity is important. And what we want to achieve is a simple thing: displaying some information to a user. So why all that complexity?
So, let’s take that step back. Does it make sense to learn so many new techniques for a relatively simple task? Don’t get me wrong, there are certainly many situations where it makes sense to do this. But not every application needs a full-blown SPA, and some solutions may be better off if they don’t.
When not to build a single-page application
Whether or not to build a single-page application depends on a few factors. I’m sorry to disappoint you, but there is no single answer to that question. However, there are several factors to take into consideration.
1. Loading Time and First Contentful Paint
A single-page application is built completely in Typescript or Javascript and packaged as a bundle with all the dependencies that it requires. This usually results in large bundles of Javascript - even when minimised and compressed. Those bundles need to be downloaded by the users of your application before they will see anything. After that, the Javascript engine of the browser needs to evaluate that Javascript bundle.
Only then, the user will see something on their screen: some kind of placeholder, decorated with spinners or indications that data is being loaded. Loading that data means executing yet another HTTP request. In a corporate network, this may not be a big issue. Users who access your application over (potentially slow) mobile connections will definitely observe these delays.
This factor is called the First Contentful Paint or FCP: the time between the user first navigating to a page to when any part of the page content is rendered on the screen. Content, in this case, means text, images or anything like that. So even a preliminary user interface, with placeholders for actual data, counts as achieving the FCP - but users usually don’t care about placeholders.
Have a look at Lighthouse if this sparked your curiosity. It’s an easy-to-use tool to measure the FCP - and give you lots of other valuable hints for improving the quality of your web pages.
2. Search-Engine Optimisation
As said, a single-page application is delivered to the browser as a big bundle of Javascript code. This poses a challenge for search engine crawlers. Crawlers are the applications that browse the internet to feed search engines with information about which pages exist and what content they serve. These crawlers in turn need to do what your web browser needs to do: download the Javacript bundle, run it, and see what happens. That is a complex and error-prone process, with the possible outcome that the search engine that runs the crawler doesn’t have a good understanding of the content of the pages it has requested. A crawler that doesn’t understand what your page is about will not be able to recommend it as a search result to people who may very well be interested in your page.
3. Accessibility
Accessibility, sometimes abbreviated to a11y (a - eleven letters - y) is the degree to which a software system can be used by everyone. Unfortunately, this isn’t something we should take for granted. For example, if a user is visually impaired, they will not see that a part of your markup is styled in such a way that it looks like a menu. They depend on a screen reader to read the page out loud, or on a braille reader to be able to get the information. These tools usually have a hard time interpreting single-page applications, let alone expressing the intent and meaning of content they encounter. How would a screen reader be able to express the fact that a list of bulleted items is, in fact, a navigation menu, but the bullets shouldn’t be showing (because they were hidden by CSS)? Of course, all major JavaScript SPA frameworks have guidance on how to achieve that. But the fact remains that such applications heavily rely on JavaScript to show, hide or animate parts of the Document Object Model (DOM) to express semantic meaning. As you may imagine, this requires additional processing for the supporting software or device and additional effort when building such a solution.
4. Security
Furthermore, a single-page application brings new challenges to the table when it comes to securing your application. Granted, the user interface itself may not contain much data, but that data needs to come from somewhere. That “somewhere” is often an application programming interface or API, exposed over HTTP, following paradigms like REST or GraphQL. Such APIs need security mechanisms, too, but they are often designed to be stateless. Stateless here refers to the principle that such an application minimises the amount of information it keeps in memory between processing requests. That is exactly how web applications were secured, traditionally: by keeping some information about the logged-in user in memory and using that every time the user requested another page. The stateless paradigm makes it again a bit harder to properly authenticate users and authorise them to access that data.
5. Graceful Degradation
When JavaScript wasn’t as widely spread as it is today, it was already used for small dynamic elements on a web page. Since not every user would have JavaScript enabled in their browser, it was considered a good practice to have graceful degradation. In this context, graceful degradation refers to the fact that a system can offer limited functionality even when parts of it have been disabled or rendered inoperative. But… how to gracefully degrade an application where the use of JavaScript is completely fundamental?
You might argue, but who has JavaScript disabled these days? And you might be right - at first glance. But look again at reasons two and three: web crawlers, screen readers, braille bars, they all have smaller or bigger issues with JavaScript. For those users (human or not), your web application might have been a lot easier to grasp if it didn’t rely so heavily on JavaScript.
But there’s more to this. Even in a world where everybody has full-functioning JavaScript available, we have to be aware of the “fallacies of distributed computing” These fallacies describe common misconceptions or misbelieves about running computer programs in a distributed environment, such as the Internet. I recommend you to read through those fallacies, and especially the effects of these fallacies. Chances are you’ll recognise quite a few from projects you’ve worked on… Ideally, however, distributed software - such as SPAs interacting with remote APIs - should take precautionary measures to address each of these fallacies.
But There Is A Solution For That…
Many of you will now think that there are already solutions for the above problems. Indeed, a lot of these challenges can be overcome by the most popular JavaScript SPA frameworks. That is, if you put enough effort into applying them correctly. The funny thing is, that some of these measures rely on the classical, multi-page approach to building web applications.
For instance, let’s look at “Loading Time and First Contentful Paint” and “Search-Engine Optimisation”. Both can be addressed by applying server-side rendering. It means a lot of the processing that would typically be done by the client (e.g., the visitor’s web browser) is done by the server, and cached for the next visitor. This helps web crawlers as they will see a populated page with all the content in the right place. It also helps improve the FCP as the visitor will initially load a fully populated page, and then asynchronously download the JavaScript bundle that adds behaviours, interactions and such.
But isn’t it funny to realise that we’ve been building web applications with exactly this approach for decades?
Boring Web Applications
In this approach, also called multi-page applications, traditional applications or as I jokingly call them, boring applications, things work a little differently. Simpler, if you ask me. And in a world where we easily over-engineer projects, adding complexity even without a business driver, I feel it is good to sometimes reflect and ask ourselves: do we need all of that? Or can we keep it simple stupid?
Let’s revisit this pattern:
- The web browser navigates to a particular page and requests it from the server.
- The server usually reads a file from disk, maybe filling in some variables (this is called “templating”).
- The server returns the complete HTML to the browser.
That’s it! As soon as the first pieces of the HTML return to the browser, the browser can start rendering the page, adding content and elements as the response is downloaded. But in today’s world, that download is often fast enough to make it impossible for the human eye to witness the page being built up. It’s “just there” before you know it!
And the good news is, in many cases, this is all you need. Often, we simply don’t need the most interesting, dynamic, demanding user experience to exchange information with an end user. This means we don’t have to drag all the complexity of a JavaScript framework, a separate ecosystem with build tools, linting and packaging into our project. Of course, there are situations where it makes sense, for instance, if you must meet high customer expectations in terms of user experience.
Reviewing simplicity
I believe the challenge is to keep it simple stupid.
In other words, if you’re not sure what to do yet, do the simplest thing that could possibly work. Adding complexity later is relatively easy. Removing stuff you don’t need in hindsight is a lot harder.
Here, the YAGNI principle applies. Every time we think about something and say things like “weโre probably going to need this someday”, it means we got distracted. We weren’t thinking about what we need today, and that should be our goal when building software.
Next to YAGNI, there’s the famous 80/20 rule or Pareto principle. In this context, the rule says that roughly 80 per cent of the benefit comes from 20 per cent of the work. Our job is to find that simplest 20 per cent and do that work - especially in situations where resources, such as time and money, are scarce. When it turns out it works, and gives that large benefit, we can always decide to put in more effort to gain the remaining 20 per cent of benefits - if we feel it’s worth it.
Examples of Boring Applications
Let’s look at two archetypical examples of what I would call a “boring application”.
Please note, that this is not to say that every instance of such an example would qualify as not interesting. They solve real issues, real business problems. It merely says that such systems may have relatively low demands in terms of dynamic behaviour and user experiences.
Management Dashboard for Integration Solutions
Let’s look at the first example. Imagine a system whose main responsibility is to provide integrations between other systems. Think of transferring files or messages from one system to another, possibly transforming them on the go.
In such a scenario, the user wants to have a one-stop view that tells them “everything is running fine”. And where things don’t run smoothly, offer a way to re-process a particular message or file. Such a user interface doesn’t need complex user interactions or rich animations. It’s sufficient if there’s one page that shows the “one-stop view”, another page that allows inspection of the error backlog and maybe one to look at a particular exchange and inspect its details. If the user wants a live overview, a one-line change in the overview page would suffice:
<meta http-equiv="refresh" content="5">
Expense Reporting
If you make some expenses for your work from your pocket, you probably want them reimbursed by your employer or client. The process is relatively simple: for each thing you have bought, you describe what it is, how much it has cost you, and maybe who in the organisation has to pay for it. After that, you submit your expense report. Of course, you can make all of this with rich, dynamic user interfaces - but is that necessary to implement the business need? I don’t think so! All I want is my expenses reimbursed, and I honestly couldn’t care about spinners, animating list items, dynamically calculated subtotals or whatnot. If I had to build such a system, it would (again) be a handful of pages. And if I really needed that “rich” user interface, well, I could write a few lines of JavaScript on the page(s) that require it. Certainly not many hundreds of kilobytes!
Wrapping Up
This blog hasn’t been too technical - yet. I hope to add a Part II soon that dives into today’s choices in the Java ecosystem for building such simple web applications. Stay tuned!
In the meantime, if you’re looking for a more lighthearted version of this topic, I can recommend “What Is A Single-page Application?”.
For now, I hope I made you think about the question: do I need all that complexity? And I’d like to challenge you for your next project, be it professional or hobbyist: how can you make it as boring as possible ๐.