Legacy-proof UI: Part 7 — Decoupler and future implications for legacy-proof UI code
Table of Contents
Over the course of these articles, I am reflecting on why UI code is much more prone to become legacy as opposed to other types of software. As I identify the reasons behind this phenomenon, I propose an approach — not the most obvious one, but, as I will do my best to justify, is key to opening the door to making UIs legacy-proof and scalable as well as offering extra perks.
- Part 1 — Why does code become legacy?: By using a simple metaphor, I try to illustrate the problem and use it as a reference for the entire series
- Part 2 — Why is UI development legacy-prone?: Another illustration that builds on top of the previous one, further clarifying the problem
- Part 3 — How to make UIs testable and easy to change?: A concise survey of known approaches to UI in the context of the problem outlined in the previous articles and where they fall short as well as proposing a new solution
- Part 4 — React as a decoupled stateless view in Storybook: In this article, we will consider an example of what it means to have a stateless view separate from business logic.
- Part 5 — MVU architecture in a React application: Example of how to make a completely stateless view interactive
- Part 6 — From Figma to React, Vue, Svelte, Preact, and Beyond: Exploring the legacy-proof (adaptability) benefits of having a completely separate view that matches designs closely
- Part 7 — Decoupler and the future for legacy-proof UI: Further exploration of perks that this approach allows that go even beyond legacy-proof code considerations
Over the course of the previous articles, I was trying to answer the question of how to write legacy-proof code.
The main point was to achieve two things:
- Get the correctness feedback quickly
- Make use of good design patterns
Albeit, there could likely be many other solutions, I considered the MVU pattern.
This pattern allowed us to take advantage of Figma as a declarative view generator and storybook as a quick correct feedback mechanism to check UI.
This pattern also made the application logic easy to test in a black-box manner.
IO-update MVU is a very flexible pattern. And with every flexible tool (like Redux or React), there are too many ways to shoot yourself in the foot.
To use this pattern wisely, you really have to know what you are doing. And given the time constraints of projects, most of the time you won’t be able to think deeply about how to make sure the legacy isn’t born.
Design as the single source of truth and the core asset for communication
Designs should be the core asset for reasoning about the application (not the architecture) because developers, designers, and business representatives can now reason about direct interaction with the product being built.
If UI is the main source of truth, then it is much easier to map the concepts and intentions to code based on the structures used in designs as opposed to business required in traditional OOP practices that blur the notion of what is an object, and where one begins and ends.
Essentially, design already has the necessary entities and we don’t need to introduce extra ones from user scenarios, which makes it much easier to understand what entities we need in code.
Division of labor
This approach allows to separate design and layout concerns from logic.
So that you only develop logic only when designs are ready.
And, usually, this makes it much easier, as the majority of expectations could already be demonstrated in Storybook.
This approach might allow to have a dedicated person for refactoring. That is, as a developer, your task is to only make sure your code passes tests, and the purpose of the dedicated refactoring specialist is to make sure everything is consistent with the patterns. Usually, such people are very opinionated, so it’s better to make sure it’s one person to avoid arguments.
Having a dedicated person for refactoring would make the code review process lighter and less prone to bottlenecks, as the main criterion for working code is whether the tests pass.
Declarative low-code tooling
For better or for worse, to avoid the trap of too much flexibility for a lot of simpler use cases, frameworks should be introduced.
I believe that the MIU pattern allows for the creation of low-code frameworks similar to what we see with Unity and Unreal Engine.
This pattern could allow us to build UIs using declarative tools in an IDE, where logic could be composed similar to how you compose scripts in Unity, Cloud Serverless functions, nodes in Blender, or Blueprints in Unreal Engine.
But even without the low-code tooling for business logic, having the ability to generate the view layer from designs already eliminates the duplication of effort of transferring designs to code.
Regarding the IO, there could also be low-code tools. Swagger could be taken as inspiration. Swagger codegen is a great tool that allows you to declaratively produce code to interact with APIs.
We could introduce other similar low-code visual tools to connect various parts of the app and produce code based on these declarations.
And to build on top of this even further, why not introduce a dedicated IDE for this task? After all various mobile platforms as well as game development platforms do exactly that.
Not at the present stage of the technology, but in the not-so-distant future, it seems plausible to expect that given black box pure-function tests of your application, you could feed them to an AI model and get the implementation that would satisfy these tests.
This would make development super easy.
In fact, ChatGPT already introduced its code interpreter that attempts to do a similar task of passing tests iteratively running its own code, and getting feedback from the interpreter.
It would also allow us to fine-tune constraints for low-coupling and high-cohesion declaratively. So that the AI agent turns it into an optimization problem.
It definitely a possibility now to use AI to help with some parts of the coding.
If we rely on Figma as a declarative view generator along with MIU pattern, we could export our view in a format of any static templating engine, so that the backend wouldn’t have to do excessive server-side rendering logic but instead do something that is very natural — generate static html with parameters given and not requiring the complex configuration or setup or reliance on frameworks for that.
Server-side rendering is notoriously complicated and being able to simplify the problem, we could save time and effort.
It seems ironic, that at the end of my search for legacy-proof code, I arrive at a solution that tends to eliminate code altogether and instead introduce declarative tooling that provides a quick feedback loop about how correct everything is.
The IO-update MVU pattern embodies this shift, offering a straightforward way to handle UI and application logic. Separating the design realm from the business logic realm.
However, let’s keep things in perspective. MIU is an effective pattern, an effective tool, not a magic wand. It’s designed to make sure code can adapt quickly, with some useful side-effects that allow to improve team communication, streamline the onboarding process, and foster efficiency through low-code tooling.
As attractive as the notion of AI-assisted coding might be, we aren’t there yet.
So, our focus should remain on making the most of the tools we have today.
Creating legacy-proof code isn’t a destination but a journey. It’s about gradual progress, learning, and adapting as we go.
I believe that this MVU pattern is a promising step in this direction — a useful addition to our toolbox that can help make our code more maintainable and robust, however, only when it goes through baptism with fire — when it’s used to create something large, only then could we see its true pros and cons.
Also, I believe that if we formulate problems as optimization and mapping problems, then solutions would no longer be a matter of opinion but rather something measurable. I believe that this pattern has the potential for formulating business logic in this way, where we could specify constraints and solve them (constraints like expected results, coupling, cohesion, performance, etc) via an automated process.
I hope you enjoyed this path we covered over the course of the articles as much as I have.
Let me know your thoughts in the comments!
- Decoupler-MVU Documentation: I created this docs to highlight projects that consider using this pattern
- Elm Architecture Guide: This guide presents an introduction to the Elm Architecture and provides useful details on how to structure applications effectively.
- Model View Update in Elm: This tutorial explores the model-view-update pattern in Elm, a key concept for building interactive applications in the language.
- Swagger Codegen: Swagger Codegen is a tool that allows developers to generate client SDKs, server stubs, and API documentation from an OpenAPI Specification.
- OpenAI’s Blog on ChatGPT Plugins: This OpenAI blog post provides insights about ChatGPT plugins, which extend the functionality of the ChatGPT language model.
- Are Unreal Engine Blueprints No-Code?: This article discusses Unreal Engine Blueprints and their position in the spectrum of no-code solutions.
- Vercel’s Guide on Serverless Functions: Vercel’s documentation provides a comprehensive overview of serverless functions and their implementation on the Vercel platform.
- Azure Functions: Here’s the official Azure Functions product page, where you can learn about the features, benefits, and pricing of Microsoft’s serverless computing service.
- Working with Scripts in Unity: This Unity tutorial presents an in-depth guide on working with scripts in the Unity game development platform.
- Scaling Up with Server Side Rendering in Vue.js: This guide from the official Vue.js documentation provides a comprehensive tutorial on scaling up applications with server-side rendering.
- Client-Side Rendering vs. Server-Side Rendering: This FreeCodeCamp article discusses the differences between client-side rendering and server-side rendering, explaining the pros and cons of each.
- Building Your Application with Server-Side Rendering in Next.js: Next.js’ documentation provides a guide on how to build applications using server-side rendering, a technique that can optimize the performance and SEO of your web applications.