Witchcraft & Wizardry – The Scripter’s Stone
In this series I will be writing about front end development explicitly & how you can go from a zero to a hero with writing front end applications that include a lot of complex state. I thought that as I’ve recently been working on a very complex single page application, one of which includes an insane amount of state, I thought it’d be a good idea to share some of the ideas that I’ve thought of or some things that I’ve learned on my journey.
I plan to make this an 8 part series, much like how there are 8 Harry Potter movies. Ironically I’m not a hardcore Harry Potter fan, but I thought that it might be fun to go with a Harry Potter theme for naming the different volumes & I thought that it may help make things more interesting for the readers! 🙂
I would recommend this series of blog posts for some intermediate or more experienced developers, some of the subjects that are addressed aren’t really very beginner friendly. If you’re just starting out, I’d suggest that you start elsewhere.
So, to keep things simple, I’ve decided to talk about the following subject areas:
- The beginning.
- Some basic topics around cache
- Some simple examples around events & avoiding event conflicts
- Shortcuts, enabling power users to get their work done ASAP
- Notifications, keeping the user informed & up to date
- State machines
- Undo, redo & refresh state, enabling the user to correct their mistakes
- Immutable versions, avoid some nasty side effects
- Optimistic assumptions, don’t keep the user waiting
- Multi threading
- Freeing up resources, in turn speed up the rendering
- Complexity & managing computationally complex functions
- Request, running many and/or long running requests
- Large payloads & view models
- Modals, draws & more
- Are you sure? – Make no assumptions on behalf of the user
- Secondary components, do your users make use of this feature often?
- Ribbons, another place to hide complexity
- Responsive VS alternative, does a mobile device really need that functionality?
- Legacy support
- Application versioning, upgrading your tech stack, don’t leave your users in the dust
- Legacy web browser support, if it works in IE11+ you’re probably all good
- Evolutionary architecture, enabling the best of both reusability & brand new features
- Performance uplifts
- Small & simple, good things come in small packages
- KISS, let’s assume the user is looking at the web app on a toaster
- More caching, why load the same thing more than once?
- Fake it till you make it, more on optimistic assumptions & evolutionary architecture
- Styling & emersion
- Animations, animate your web application, give it some life
- Absolutely! – Positioning & transformers in disguise
- Theory, don’t over think it & keep it simple
- To infinity & beyond
- PWA, progressively wicked awesomely-amazing-applications
- Change logs, change requests, change!
- Architecture, let’s build it like a pro
Lesson 1 – The Beginning
I hate to be one of those guys, but like everything that turns out well, within the world of software development, some thought has gone into the design process. This doesn’t even have to strictly be a technical subject matter, this is in fact a domain level subject matter, so in theory, subject matter experts may be able to provide a lot of value here, even if they have a non technical background. Like traffic lights, you may not need to know how the electronics work, but you have a high level understanding of the different states that a set of traffic lights can reach. 🚦
With regards to your application state, this may include the likes of thinking about known states, these may be ‘stable’ states, where it’s some state that you have expected to reach, or it may be some unknown state, such as an unforeseen error has occurred within your application.
Let me just dish you up a very small example, just to give you a taster of what I’m getting at here.
This is simple enough to follow, we have some states in which we expect our application to reach, maybe some of them are not good states, such as error or denied. But the point being that these are states that we’ve thought about beforehand. These are states that we know the application will reach for the right combination of input. We could add more forms of state to this again, e.g. rather than generalising error, you could include ‘user error’ as a known state, implying that some input is invalid. You could also include another error state called something like ‘server error’ which implies that the server responded with some 4xx or 5xx status code. The key is to really think about the potential forms of state & how one state may impact an action.
Given the application that you’re working on it might not hurt to think about developing reusable components like a generic modal component that you can reuse for confirming the likes of deleting something within the application. I find that it never hurts to make sure that the user is certain that they want to delete something. While you should allow a user to recover from deleting something, at least that’s my philosophy, you should also double check that the user is certain that they’re aware of the consequences to their actions.
Let’s look at something like this as an example:
Personally I’ve found that in the past, it’s best to create some function like the previous example, where there is a function dedicated to creating the state. Now given that state is a vastly complex subject area, you may want to create a factory implementation for creating your state object, where the state that you’re looking at for a given feature or component may have some dependency on some state elsewhere within the application, e.g. let’s look at products on some imaginary ecommerce platform:
Sure, there are aspects of the example that are extremely basic & primitive, but the example is only there to paint a picture, to demonstrate where you can go with this. I’ve found that if you’re very good, you can create some very well defined & self documenting code, where each property of the state is defined within these functions, this way, for any potential state, the developers know what varieties of state the application or unit of code can reach. Not to mention, personally, I’ve found that it can even make unit testing that little bit easier.
Caching & Diffing
To clarify, you may have a form, you may want to include auto save as a really neat little feature, but to reduce pointless calls to your server, you want to ensure that you’re not pointlessly making some PUT/POST request to some endpoint that saves the users information. This isn’t some ingenious idea by any means.
So how would we do this exactly? What would be the best method around this approach? 🤔
The answer to that question is to have some relatively primitive diffing logic going on, it doesn’t need to be super intelligent, it doesn’t need to blow the minds of computer scientists around the world, all you need is to know if the state has changed & if the state is currently different to the original state. You don’t want to allow the form to save the user input if they’ve just entered something that differs from the original state, but then the user has pressed ctrl + z or something, which now means that the form is back to it’s original state. Front end developers need to be a lot more intelligent than that & the way the world wide web is going, users expect a lot more from developers & engineers.
As you can see in this silly little example above, we’ve got some pretty basic caching solution, which will detect to see if there is some difference between a given state & the most recent or the new state, you can run this diff solution to see if this user form has changed from one point to another.
For the sake of everyone’s sanity, I won’t cover more complex areas or concepts surrounding caching or diffing, because within this subject area, one could write a PhD level thesis. Caching can be extremely complex, same to be said for writing some super sophisticated diffing algorithm, the example provided above merely get the fundamental point across.
As this is lesson one, I thought that it’d be nice if we ease our way into the complexity & the more you dig into the subject areas mentioned, the more complexity develops, the more you combine these subject areas, the faster the complexity grows.
Of course, like every decent front end engineer, you’d want to have a pretty good idea on when you’d want to fire these methods, rather than just polling this method via a set interval function, it’d probably be wiser & more efficient to run such logic when a field triggers an on blur event. This might be for each field, or whatever, it could include some very clever & complex logic, such that it’ll allow the user to press a save button manually, where it can state the last time that the application was saved.
Alternatively it could include a hybrid approach, where the blur callback may trigger some set time out function, which if the state matches all the necessary conditions, one can auto save, or wait for the user to hit the save button, e.g.
You can see that even though we have some handlers that would in theory conflict with one another, specifically if you look at the function I’ve cleverly named test 3, that one shows how even though they’d both be calling the save form function, the save form function only executes 3 times. We know this because by running this within our console within our web browser, we can see that the console log is only run 3 times, no more, no less.
When thinking about events, you should think of ways in which you can make the user’s life that much easier, especially if you have power users that interact with your software nearly all day, everyday. Keep in mind that humans are creatures of comfort, we like things that don’t require much thought, myself included, when interacting with a UI, I don’t want to have to sit there & seriously think about how it’s going to work. But at the same time, I don’t want the UI to make too many assumptions on my behalf, getting this right is an art form in its own right.
Let’s take keyboard shortcuts as an example, if you’ve used enough web applications out there, you’ll know that one common short cut is ctrl + f. But this is also a short cut to use a feature within a number of browsers, if not all browsers. So what do a lot of us nerds do? It’s simple, by default, we keep track of if our application has taken advantage of the user entering such a shortcut, provided they have, then we can allow the user to make use of the feature within the browser itself. This can be an annoying feature, but as a power user of some application(s), I appreciate the thought also.
Google Docs Example
When you open up Google Docs & you’re working on some text base document, if you hit ctrl + f, you can see how Google Docs will open up their little component that allows you to search for something within the body of the document. This is completely reasonable.
But I’m not sure if this is a bug, at the time of writing, if you press ctrl + f again, it doesn’t allow access to the underlying feature within the browser. I personally can’t say if this is a design decision or not, but the ironic thing that makes me think, maybe it is a bug is that when you click on ‘file’ & then hit ctrl + f, you can then access the feature within the web browser, rather than being limited to the scope of the document, you can now find some text anywhere within the UI.
But like Google Docs & many editors around the world, it’s a reasonable claim to state that ctrl + s means that you want to save something. This is familiar to many users, technical & non-technical alike, in beautiful scenarios like this, it just makes perfect sense, if you’re building some software that takes into consideration a lot of data, then it makes perfect sense to enable the user to manually save by using this short cut. Sure, you can have a more long winded approach to enable the user to save whatever it is that they may be working on. But prioritise your power users! 🤘
By todays standards, there are many ways in which a web developer can notify a user that something has happened, be that email, sms, via an app, push notifications, etc. Your imagination may just be the limit here. But generally we agree that when building web applications, it’d never a bad idea to use the likes of a toast notification. By referring back to how you the developer should plan for known states. This is one case where knowing about what state has been reached, in addition to perhaps a message to accompany the known state, we can notify the user by using some common colour(s), e.g. green meaning success, yellow meaning there’s a warning, red meaning that there was an error, etc. Of course this will depend on your colour theme(s) & it will also vary based on opinion & how you go about applying colour theory. 🎨
If you know Angular syntax to some degree or another you can get an idea of what’s going on here, we can see that the controller is aware of the different forms of state in which the state machine can reach. Depending on the state machine & the current state that it has reached, this will have a knock on effect on how the toast notification is being rendered. In this exact example, it’ll decide whether or not to output a message that may be generated by the controller, it may decide to use a default message depending on its current state, etc. Depending on the state, some styling will change, because wee can see that the CSS class will change based on its state, the font awesome icon that’s displayed will also change depending on the state of our state machine, etc.
More on Context
Again, by using some common ground, knowing commonly used icons can help provide context & further the users understanding about the current state of the application. E.g. The view model may have updated, which results in the UI being updated, but who’s to say that the request to the server went according to plan? What if something wen terribly wrong? How would the user know about this? It’s simple, we notify the user.
By talking more about familiarity & standards in software & UI design, it’s good that we keep in mind picking the likes of icons, wording, colours & so on. Patterns, much like developers, a subset of humans, we all like patterns that we can commonly agree are fit for purpose for a given context.