1) Your user interface probably has more edge cases than you or your designer anticipated, and IME the most efficient, understandable way to deal with these is in as-simple-as-possible plain-old-code if/else business logic. The further away you keep code like that from anything "frameworky" the less likely you are to regret it as tech debt.
2) Many of your state transitions probably share similar helper logic. So you want to break that stuff out into its own non-state-based organization anyway to avoid copypasta, and at that point, similar to the above, representing different use cases as linear code as much as possible is a low-cognitive-overhead way to organize your biz logic. That is to say: I think your "states" should be big and chunky - occasionally one feature's flow will transition you into a different feature, but whenever I've tried to push that model down to the individual screens and buttons level, it's caused self-induced pain.
If I had to reduce it to a soundbite, I'd say: your business logic is likely to be inherently complex. Don't intermix that complexity with the technical complexity of your application framework if you can at all avoid it.
(Where this KISS approach goes wrong in practice is you don't have enough team discipline about organizing your biz logic code, or you try to share the wrong things/too early, and some biz logic ends up in every layer of the UI, and you get lots of big pull requests touching lots of files with hot spot files frequently having merge conflicts.)
I think the big misconception here is that statecharts _have_ to be framework-y. They don't, and their biggest benefit (in my opinion) is how they shift the mental model of creating UIs from "bottom-up" to "top-down", so that even if you're not explicitly using finite state machines or statecharts, your UI benefits from better maintainability.
Here's the thing. As developers, we write implicit state machines in our UIs all the time, in the form of boolean flags, or enums if we're feeling disciplined, or peppered if-else statements everywhere. Arguably, most (if not all) UIs can be represented as explicitly classifiable states, and actions that can occur that can transition those states into other states.
So I'd argue that having _some_ sort of declarative formalism for describing your app's states and transitions is much more beneficial than having an ad-hoc way of imperatively changing states, which most of us developers are wont to do, since it's "easier" to implement.
Since statecharts are hierarchical graphs, there's also the possibility for automatically analyzing, optimizing, and generating full integration tests for user flows given a statechart spec.
I agree that trying to use statecharts everywhere is fitting a round peg into a square hole, but thinking about the UI in terms of states, and actions in terms of something that is simply emitted instead of something where application logic is stuffed into, will greatly improve how UIs are developed in the future.
I'm confused because statecharts don't describe business logic - you might be confusing them with flowcharts which are inherently very different: there's no concept of state in flowcharts.
> the most efficient, understandable way to deal with these is in as-simple-as-possible plain-old-code if/else business logic. The further away you keep code like that from anything "frameworky" the less likely you are to regret it as tech debt.
A statechart is simply a JSON structure. You still have to write the code for the figuring out what to do given the current state, and also code for triggering events. So it's still up to you to keep them away from anything frameworky.
> representing different use cases as linear code as much as possible is a low-cognitive-overhead way to organize your biz logic.
Again, you'd still write linear code with statechart.
> your business logic is likely to be inherently complex. Don't intermix that complexity with the technical complexity of your application framework if you can at all avoid it.
With statecharts your business logic is anywhere you want it to be. Statecharts add the idea of limiting you of calling only procedures that are allowed by the current state; meaning that if you are in state "Submitting form" and you call a `goToHomePage()` transition, nothing will happen if there's no such outgoing arrow.
This concept of "changing state only if the current state allows it" is quite powerful especially in the construction of UIs.
Visualization is a powerful mental technique, but visual aids can only really represent a few dozen things before they become as complicated as the thing you were trying to understand in the first place.
And when analyzing messy state diagrams, it’s not just the states we’re trying to visualize, but the lines connecting the states (the “edges” connecting the “nodes,” in graph-theory terms). We can only visualize a few dozen of those, and that typically means we can visualize only a handful of states at a time.
It looks great in a slide deck, but it only works in trivial examples where you don't need it; it fails in complex environments where you need it the most.
I disagree especially because statecharts solve this with hierarchical states. You draw and think about a substate on its own, without all the higher-level complexity. Then once ready you can include it in any subsequent layer - hence it’s composable and reusable.
Take a look at the “unclustering” section of the statecharts paper.
Yes. I can this a self describing system. When the code or config can be used to generate reference materials, at run time or build time, to make understanding the system easier.
Typically you don’t need to refere to these diagrams and outputs very often, but they are useful for:
- on boarding new people to the team
- deep reasoning when debugging or analysing a problem
- putting on the board during a design session
State is typically multidimensional, so being "in" some state implies a whole lot of context (a whole lot of variables), and most real-world situations are so loaded with context that the only unambiguous way of determining which real-world variables are represented within your program is reading the code (or inversely, any unambiguous definition of which real-world variables are relevant to your program may as well be code). This limits the value of statecharts IMO. I think a more useful solution is something that describes how each level of state is derived from the previous level.
Of course to understand how things are implemented you'll have to read the code. With statecharts at least you have a visual bird's-eye view of the behavior of your UI. This is important because otherwise you'll be lost with just code to figure out how things behave.
More importantly, statecharts limit your behavior - a specific state can only go into another state if there's an outgoing arrow. This limitation is quite awesome because, given any state, you know that the only thing effecting change are the transitions - nothing else!
Also I want to stress the importance of the unclustering property described in the paper. Your high-level statechart can be a simple diagram. Then you can "zoom in" and see the behavior of other portions of the diagram. This is quite powerful abstraction and I don't see how "just code" can solve this - especially because we're inherently visual creatures.
I'm definitely not arguing against the statechart being higher-level than a pile of state mutations. I think they're very low-resolution however, and I believe there can exist code that can serve the same high-level purpose as statecharts without discarding as much information.
I also think zoom is a very powerful visual metaphor for context, and I don't mean to discount the value of that. I just think the contents of each node in the zoom-context graph could be higher-fidelity.
> More importantly, statecharts limit your behavior - a specific state can only go into another state if there's an outgoing arrow. This limitation is quite awesome because, given any state, you know that the only thing effecting change are the transitions - nothing else!
This touches on a point that I think is important and would like to expound upon.
One thing that makes code easier to reason about is "locality", being able to see everything that affects what you're looking at.
One way of dealing with poor locality in code is to zoom out until you're only looking at a web of state transitions, and then you can find the state you're in and see where you could have come from (which offers hints as to how you got there) and where you could go (which offers hints as to what might happen next).
Another approach is to write high-locality code, code which describes the incoming arrows so that by design, whatever you're looking at is already revealing its composition, and fractally-organized at that. That can be hard to do in application code with existing tools, although (shameless plug) I wrote libraries to help make it easier in Clojure and Javascript: https://github.com/notduncansmith/factfold
The Qt makers seem on the same page. Qt has support for SCXML [1][2], which is an XML format to represent statecharts FSM. You can edit SCXML files from Qt Creator [3], load them in your app and use them to drive your Qt GUI, or any dynamic behavior in your app.
From prior experience working with non-UI statecharts:
1) State transitions are essentially "goto". Structured programming offers abstractions that are easier to think about and less fragile to requirement change. But in some situations small state machines can be a win.
2) Statecharts offer "structure" in the form of substates and superstates which are much like inheritance in OOP. In my experience these are fragile to requirement change.
3) Statecharts are advertised as being explicit and therefore easier to check for correctness. They can be, although if there are multiple state machines operating concurrently or even an "outside world" acting on them, the set of possible states is no longer explicit.
It is a neat idea to separate the semantics and the syntax. However, for some reason, it didn't get enough popularity.
Apache Commons has an SCXML package which parses scxml statechart format (standard) and produces dynamic java objects but this couldn't see the daylight and suspended at version 0.9 in 2015.
I had nice experiences with this separation logic though. It is more flexible and extendable than regular finite state machines. There is parallelism and timeouts built-in the standard and as mentioned in the presentation, it solves a lot of if-else kinda structure without a hassle.
Not the next big paradigm because it already exists for critical software and UI [1]. For verification purposes, thinking in state charts at design level is essential. The fact that code does not reflect easily this model is not an issue provided you do not write this code manually, but off-load this to a compiler front-end. It is actually the only sane way to keep state space and performances under control.
Using state machines in the UI layer is good idea, but problem with trying to present everything with pure state machines is that the state space easily explodes when you have a lot of different potential events and interdependent systems affecting the UI.
Feels like FRP. But is it isomorphic to it? I once started with a 'back of the envelope' experiment with a frp gui system and ended up with the conclusion that I could not do without hierarchical state charts.
So, a statechart formulation could be an 'emergent' , most simple way to formalize a Xerox Alto-descendant GUI system.
The fact that they start from an existing deep GUI stack is a bit distracting to the disucssion, though...
Interesting take. I do think state-charts (and state machines) can potentially be a huge benefit to both design and development. However, your login/auth examples are really poor fits for this kind of solution. While you could use statecharts, you could also just use generalizable global form validation. There's not a lot of benefit in defining statecharts for common UI paradigms.
That said, here's an example from my own recent experience where formal statecharts/state machines became a necessity to even move forward.
My colleague published a finite state machine for swift (https://github.com/softwarenerd/Stately) that grew out of necessity from a project we worked on together.
From a workflow perspective (and UI/UX) it was never terribly complex. There are a handful of primary actions, with a handful of intents and a single common workflow with branching outcomes. Over time and iterations, we reduced the workflow complexity quite a bit further as well.
But we kept having more and more functional regressions and exploding bug counts as more and more subsystems and capabilities were integrated into the experience. The issue was that, while workflow and UI were both simple, the number of potential application states exploded.
For a long period we used simple if/then logic, followed by tracking increasingly global variables to detect state conflicts/dependencies during flows. This was messy at best and became an unmanageable nightmare.
Enter the state-chart. We followed nearly the same process as you've outlined, with visual charts first. It gave us the initial frame to build a state machine from, followed soon after by sub-hierarchies. It worked well for us because the majority of our states were not many:many interdependent. Most states had a 1:1 or 1:few correlation with others.
Statecharts likely aren't the next big UI paradigm because most applications are driven by workflow (Line of Business apps) or by state machines that are already well-known (form validation, auth, network graph navigation, CRUD, etc) where those charts are already implicitly well handled by browsers, services or existing frameworks.
I think the magic area for statecharts is in applications with many independents or semi-linear state workflows that don't match up to visual or experiential interaction workflows.
Flowcharts don't have state? Certainly some simple ones don't, but diagramming programs in flowcharts was not an uncommon method of presenting programs... well, ever. Some of the introductory algorithms in Knuth's works use basic flowcharts. In some of his notes on computer languages, diagrams akin to what you are discussing were used by some smart people. So, don't take this as a criticism saying it is a bad idea.
More, this isn't to say these aren't worth looking at again. New and old things are always worth revisiting to see if something is different in the idea or the environment that we are in now.
That said, the UML world had several runs at diagrams that could do this. Activity diagrams being the more complicated ones, but what this is proposing seems to be the standard state machine diagrams.
It was not an unfounded (well, in my opinion) view that UML over complicated things. The problem appears to be that they were too complete in their modeling of the problems. And, these diagrams only really work in a "simple" sense if the viewers and the authors have a lot of shared context between them. The more of that context you put in the diagram, the more you will eventually remake UML's efforts. (Again, you will be in really smart company.)
Next big ui paradigm would be ditching all these messy JavaScript ui frameworks and going back to real tools we had for implementing desktop applications. At the moment we’re switching the tools every 6 months and the next shiny ones are of course no better, just broken or incomplete in different places. Web apps are crappy replacement for professional desktop ui
How do I get a user to install a desktop app to complete a survey. Will they do it to take out insurance? Is it feasible to build and test and deploy and patch and support native apps, on multiple platforms, to rent a cement mixer or a snow blower?
The low barrier to entry of web apps is so great we can easily put up with the many downsides.
1) Your user interface probably has more edge cases than you or your designer anticipated, and IME the most efficient, understandable way to deal with these is in as-simple-as-possible plain-old-code if/else business logic. The further away you keep code like that from anything "frameworky" the less likely you are to regret it as tech debt.
2) Many of your state transitions probably share similar helper logic. So you want to break that stuff out into its own non-state-based organization anyway to avoid copypasta, and at that point, similar to the above, representing different use cases as linear code as much as possible is a low-cognitive-overhead way to organize your biz logic. That is to say: I think your "states" should be big and chunky - occasionally one feature's flow will transition you into a different feature, but whenever I've tried to push that model down to the individual screens and buttons level, it's caused self-induced pain.
If I had to reduce it to a soundbite, I'd say: your business logic is likely to be inherently complex. Don't intermix that complexity with the technical complexity of your application framework if you can at all avoid it.
(Where this KISS approach goes wrong in practice is you don't have enough team discipline about organizing your biz logic code, or you try to share the wrong things/too early, and some biz logic ends up in every layer of the UI, and you get lots of big pull requests touching lots of files with hot spot files frequently having merge conflicts.)