In this article, I’ll try to summarize some of the things that seem to be required to launch a web app (presumably in a business context) these days. I cannot give you any guarantee on completeness, so if you see anything missing from the lists below (or have some suggestions) please use the comments.
The list is structured by using different categories for the various bullet points. Inside the categories, two groups of items are used — either they seem to be required or are just recommended from my personal point of view. The classification may be different from your point of view.
Furthermore, I tried to explain my thought process reasoning for the category (specifically, why I think its items should be considered) in the beginning and added one example as illustration.
Before even implementing a web app, you need to think about some organizational and non-technical aspects. If the web app is developed in a team or should help to sustain a business the following list seems reasonable.
An open-source web app follows a similar pattern, even though things like backers and community acceptance are less predictable and in a slightly different ballpark.
A list of supported browsers can be specified as simple as a Browserlist rule set, e.g., “last 2 versions, IE 10”. The great thing is that this rule set can be just added to the package.json
of our web app and will be picked up automatically.
The addition can be as simple as:
Several tools exist, e.g., to export the rule set to a list of matching browsers.
Infrastructure has never been sexy, but it’s always necessary. You may have noticed that infrastructure complexity in a cloud-first, fully automated setup has increased. Unsurprisingly, so did the tooling. Having the right tooling makes infrastructure not only manageable but also fun and efficient.
Besides a state of the art CI/CD pipeline, we should definitely incorporate full error tracking. In the end, knowing what breaks and why is crucial for improving the web app. Being able to roll out changes rapidly helps us to become less vulnerable.
To include advanced error reporting we usually only need to include a script and call an init function. Sometimes more sophisticated configuration (e.g., providing user-specific metadata) is useful, however, the crucial part of the job is already handled by just calling the init function.
As an example, we look at LogRocket:
{
"browserslist": [
"last 2 versions",
"IE 10"
]
}
This is pretty much it! The rest is a (custom) mix of using the official tools to access the gathered data and the API for integration in our own tooling.
Obviously, having a solid architecture, sound business logic, and sufficient tests are required for any application to be considered stable and well-maintainable. Nevertheless, for web apps, some special rules apply.
Not only do we need to look for the smallest possible bundles of code and assets to serve (yet delivering a unique or even outstanding user experience), we also need to test our code against a variety of different browsers and screen sizes.
To make matters worse available APIs, ECMAScript specifications, and styling edge cases may be handled differently.
How we end up from plain sources with a minified, polyfilled, intelligently split bundle that can be easily cached and contains all kinds of wanted features depends strongly on our technology set. Some frameworks already come with a bundler out of the box.
A quite generic, yet easy to understand solution is Parcel. It also works against the previously defined browserlist and is framework agnostic. Out of the box (pun intended), it delivers TypeScript support, polyfills, production (minified etc.) builds, as well as hot module reloading.
Using it is as simple as running:
import LogRocket from 'logrocket';
LogRocket.init('YOUR_APP_ID');
The default output directory is usually the wanted one (“dist”).
When developing a web app we face a multitude of different target platforms. A website may be consumed in a non-graphical form. A website may be consumed by large devices with very odd inputs (e.g., only being able to go forward, backward, and into) or very small screens.
Furthermore, a website may be consumed by a variety connection types — fast, slow, limited, unlimited, and so on. If our app is to be successful, we need to find a balance that allows for most users to have a great experience.
If we run, for example, Google Lighthouse on an SPA without any noscript
element we will get a logical warning. Letting users know that the reason for not seeing anything (or not going beyond some loading spinner) can be found in missing JavaScript support is crucial for progressive web apps.
Luckily, correcting this is as simple as adding a simple noscript
message to the body
such as:
One of our primary concerns should be to create a secure and trustful app that does not offer any vulnerability neither to access sensitive data of our users nor entering our system.
This obviously starts with an encrypted connection but also includes a reliable authentication and authorization scheme, as well as proper validation for any input.
Ideally, regular penetration tests against our application are performed.
rel="noopener"
on external linksPlacing a noopener
(or even nofollow
) relation on anchor tags is straightforward.
Potentially, my background as a German (i.e., European) renders me especially sensitive to this topic, however, there is more to GDPR than just annoying “we use cookies” messages.
Understanding and valuing a user’s need for sufficient privacy and data protection is necessary to build a trustful environment.
Keep in mind that quite often the devil is not directly in our code, but in some component that we use for convenience.
A simple option for integrating a compliant cookie disclaimer is to use a handy plugin like Cookie Consent.
Web apps today are anything but lightweight. We need to transport scripts, styling, fonts, images, and potentially even audio or video. Complicated logic created for multi-target purposes with multimedia background will never be lightweight, however, that does not mean it has to be slow.
Using proper caching, lazy loading, and efficient bundle splitting can help. Only loading what the user needs and wants is desired.
Required
<link>
)Preloading resources can be crucial for smooth user experiences. As an example using the preload for our style and script we get (taken from MDN):
Place it in the head before any of the mentioned resources appear. Depending on the position in the document the effect may be rather small, however, especially for resources loaded via JavaScript the effect can be pretty drastic.
Personally, I always have strong doubts about this one. Nevertheless, we need to learn from the (average) user somehow, hence the need for some anonymous tracking.
Even more significant may be the search ranking, which encourages us to follow some guidelines for search engine optimization.
Providing a correct robots.txt
can be as simple as supplying the following text file (served with text/plain
as content-type
) in the root directory:
This file will allow any robot (e.g., the Google search crawler) to access (i.e., index) all files. Additionally, we could place rules to disallow certain files or whole directories if wanted.
Fancy additions give our app the extra touch that is needed to hook ordinary customers and fascinate tech-savvy people. They may appear in various forms, e.g., keyboard shortcuts, fancy loading spinners, useful recommendations, etc. These additions show that we actually care about our app and try to go the extra mile to make it super enjoyable.
(none)
Things like QuestionMark.js are easy to set up yet give pro users even faster input possibilities.
As mentioned already the given list does not try to be complete. It should rather be understood as a guideline to think in the right direction. Being prepared for the legal challenges, and potential problems is at least as crucial as having a sound and useful web app out there.
These links contain audits and checklists that are more complete and interactive. They certainly provide helpful checks to form a solid technical basis.
plug
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.