It took me more than 10 years to see that these 5 stages are what we need to build great software products.
It took me another few years to simplify them into this easy-to-remember acronym of FEAR MOST. However, there is nothing to fear if you have a strong engineering heart.
Stage 1 - Functional
Make. It. Happen.
Every engineering journey begins with a problem to solve. If there are solutions that can already solve the problem, use them! Don't reinvent the wheel.
But there are always some problems that are unexplored and quite challenging. It requires innovation, thinking outside the box, and grit, to find clever, or even tricky ways, to do whatever it takes, to make the impossible possible, and make it happen.
This first stage requires a can-do attitude.
Be a problem solver! Answer "yes" to questions of whether something can be done. If you don't know how yet, then say "I will find out".
Show some spirit! Don't spread negative vibes in the room by complaining or giving up easily. Even if you know it will be hard, say "I will try" first, before "It's too hard".
One way to solve hard problems is to use the "First principles" thinking:
Break down the problem into its fundamental components and core principles
Reason from basic principles to understand and solve complex issues without relying on existing solutions or assumptions
Reconstruct innovative and creative solutions by questioning conventional wisdom and exploring new approaches from the ground up, a.k.a reinvent the wheel!
The main focus at this early stage is to make things work and get the job done. This stage is usually found in a Proof of Concept (PoC) for an internal demo. Since the resulting software may not be perfect or the most efficient, it usually needs some refactoring in the later stages.
Some problem examples:
How to gradually convert a huge legacy AngularJS app to React, whereby AngularJS components are converted to React components but they can still run together with the rest of old components in the AngularJS environment transparently?
--> Create a bridge that utilizes the DOM and window objectHow to render a pivot table that supports up to 1 million rows and columns and each cell may contain numbers, text, images, charts, or mini-tables.
--> Create and render a tabular data structure with 2-dimensional virtual scrollbars that can work on non-uniform cell widths and heights by using an R-tree to manage which cell regions to load and render.
Stage 2 - Easy to use
Don't make users think.
After successfully getting it to work, our attention will now shift to the users. We want to make sure that the app is easy to use, even if it is not easy to make.
We aim to eliminate any confusion or extra thinking on the users' part. Treat them like VVIPs, or kings and queens that need to be served even before they ask. Every complaint from users is valid and should not be taken lightly.
This stage is NOT so much about simplicity as it is more about usability and understandability. While simplicity is usually focused on removing everything to a minimum, good usability is about having the right amount of text and buttons at the right place and at the right time.
Not everything can be made simple due to some problems in life are indeed complex in nature, but they can always be made easier to understand by using good design-system patterns and conventions.
Imagine complexity as an energy that can't be created nor destroyed – engineers have to absorb the complexity away from users and transform it into clever and user-friendly systems.
How to achieve it? My favorite guides are the 10 heuristics of usability and the
7 Gestalt principles.
Additional testing tips for web applications:
Do the 1000-screen test, to make sure all functionalities are still achievable using mobile screens, using browser zoom 25%-500%, and slowly resizing desktop windows from 100 pixels to 4K.
Do the slow-motion test, by choosing the slowest network throttling option in the browser's developers tool, and/or adding artificial delays into the code to make the detailed screen rendering behavior observable when various status-changes take place.
This second stage is required when creating a Minimum Viable Product (MVP) for early adopters.
Stage 3 - Robust
Once the products start gaining popularity, more users will begin using them. The next step is to make it ROBUST, which contains six important qualities (one for each letter for easy remembering).
R for Reliable: Always provide accurate and consistent results every time. Torture-test it for every possible use case and edge case. Test it using:
- 1000 scenarios, including random clicking, timezones, and sluggish network.
- 1000 data, including UTF-8 chars, nulls, and huge-size data.O for Optimized: Efficiently and economically use resources without slowing down performance. How much maximum throughput and capacity can you achieve from using a single server? Just because scalable cloud resource is available, it doesn't mean it should always be the first solution we take. Optimize SQL queries, memory usage, and CPU computation before adding more servers and scaling aggressively with Kubernetes.
B for roBust: The ability to handle errors from unintended mistakes, unexpected breakdowns, and malicious attempts, and the ability to recover from failures.
U for secUre: Prevent unauthorized access and keep confidential info safe.
- Use secure ways of authentication (e.g. SSO).
- Invalidate expired sessions.
- Authorize everything using fine-grained permission control.
- Use strong encryption with public-private key pair whenever possible.
- Encrypt data in transit and at rest.
- Remove sensitive data from logs and reports before sharing them.S for Scalable: Modular and can be easily expanded to handle increased usage.
T for fasT: Deliver results in a shorter time without sacrificing quality and accuracy. Remove the fats, and don't do stupid things in the code, like computing the same thing over and over again when caching is possible. For things that unavoidably need a longer time to complete, there should be a progress status indicator for users so they know what to expect.
This phase is what usually needs to be done when our product needs to be scalable.
Stage 4 - Memorable
Make it wow!
Embrace the element of surprise, in the same exciting way as a magician does.
Create a uniqueness that effortlessly catches everyone's attention and makes your product stand out.
Go the extra mile, where no one is expected to be there.
Mention the word or branding name that you want your product to be associated with. Repeat it whenever you have a chance. Repeat, to make it register easily in people's minds. Repeat, until the word is associated with you or your product.
It's about values, not just product.
Touch the users' emotional side by giving them extra helpful things, nice surprises, and unique experiences, above their expectations.
When people are surprised to find something above their expectations, the brain releases dopamine and noradrenaline, and the event will easily register in their memory. If it happens often enough, they will likely bring it up in their conversations with friends and colleagues to recommend our work and product.
The key is about managing expectations. Promise something realistic and deliver something more. Promise something good and deliver something amazing.
If people are used to seeing websites with standard template designs, then can we make our website design unique so that it is still easily distinguishable from the rest when seen from far away or as thumbnails?
If uploading a big file usually takes 30 seconds, then can we make it 5 seconds?
If the manager expects 3 features to be completed in 2 weeks, then can we complete 4 features in 1 week?
This phase is what usually needs to be done when our product is already stable.
Stage 5 - Sustainable
Make the code live forever, with little to no maintenance needed.
Automate!
- Create unit tests to guard the code from future developers (including yourself).
- Set up a CI pipeline that prevents merging new code if breaking the unit tests.
- Create a set of helpful productivity tools, to automate common repetitive tasks.
- Make the code generic and easily configurable via parameters, if possible.Clean code. There are several design patterns and software design principles (DRY, SOLID, YAGNI, KISS, etc.) that can be applied to make our code manageable.
Clear documentation. Documentation should be minimal because the code itself should be self-explanatory. Documentation is needed for higher-level and complex topics such as
- business rules
- executive summaries
- architectural diagrams
- state diagrams
- flow charts
- edge cases
This is what usually needs to be done to reach world-class software engineering.