Dive into the latest industry wisdom from
our experts.
Cleevio Rebranding: Why We Decided To Change
Today, we’re updating our website and brand to better reflect what we stand for at Cleevio. And even more importantly, what we can do for our clients.
Why
Cleevio always has big plans and aspirations. From building excellent digital products to creating a great place to work. As we evolved, the change was needed at last - not only in the brand but also in our services, which are now oriented towards long-term partnerships and focusing on the best value we can bring.
This growth, combined with the opening of our new office, meant that our website and branding no longer represented all the things we are doing and stand for.
Defining Cleevio
As it turns out, everybody at Cleevio had their preferences about how we should present the company. This creates a tangled web of powerful opinions on visuals. But these can be untangled – if you take a step back. To tell our story to the world, we first need to sit down and define it for ourselves. For that, we partnered up with Lukáš Burkoň, the leader of our new division, Cleevio Growth.
We conducted a workshop series on value propositions, customer profiles, and internal goals (just like we use with our clients, but for our own company!). With everybody on the same page, we achieved alignment across the company on the values and goals we should represent. Using the StoryBrand and brand archetypes frameworks, we crafted our tone of voice and brand personality: Creative Sage. Inspirational, guiding, articulate and knowledgeable.
Going Visual
How do you translate “Creative Sage” into a visual identity that can be used across a multitude of media and platforms? After all, these are two personalities that don’t necessarily match together on first look. We simply broke down what each could mean and represent.
- Sage: wise, minimalistic, lightweight, subdued, sharp, professional
- Creative: engaging, playful, inspiring, collaborative, a bit funky
Then it just meant iterating over your usual visual brand components like typography, colors, imagery, layouts and supporting elements.
Explaining What We Do
In addition to having design and engineering hard skills, Cleevio also strives to partner up with clients on the product side of their business. In the end, that means creating the best digital products with the most impact. For that, we need to explain the value we bring to the table.
Even the most powerful idea is fragile at first. However, with our 360 process, we can carefully capture it, mold it, and turn it into a successful statement of the client’s vision. Validate, prototype, test, iterate and develop. An excellent product is born.
We tapped into the 3D skills of one of our talented designers, Adam Kozel, to bring this to life. The idea and product are represented by glass structures, while our battle-tested process is visualized by solid black metal framing. On top of that, we use vibrant cursors to show the collaboration between us and the client – a key concept in every product we build.
Looking Into The Future
Launching our updated brand means a lot to us. It is as much a snapshot of where Cleevio is today as it is new ground for us to build on. Most importantly, it gives us a solid foundation to continue building true product partnerships with our clients.
Brand Evolution: Apps Builder to Digital Product Innovation Partner
Cleevio, as a software development company, has been providing businesses with innovative solutions since its beginning. From product-based software solutions to becoming a company that focuses on building business partnerships, Cleevio has undergone a significant transformation. In this article, we will explore how Cleevio has evolved and why we believe in building business partnerships as a crucial part of our future success.
Early days
The early days of Cleevio were focused on developing and delivering product-based software solutions - mostly custom-built mobile apps. This was a common approach for many software companies at the time. However, as the company matured and the market evolved, it became clear that this approach was not sustainable, nor did we like it. The market was becoming increasingly competitive, and there was a need for new and innovative solutions that could help businesses stay ahead of the curve.
Transformation
In response to this, we slowly shifted our gaze and started focusing on building business partnerships. This meant that instead of selling products, we are collaborating with other businesses to provide custom solutions. This approach enabled Cleevio to understand the needs and requirements of each customer and provide a tailored solution that met their specific needs.
Stronger relationships
Our focus on building business partnerships has resulted in the expansion of long-term relationships with our customers. This has helped us increase customer satisfaction and retention, as customers really appreciate the personalized solutions tailored to their specific needs.
"Our team is dedicated to building strong business partnerships with our customers. We understand that every business is unique, and that is the reason why we work so closely with our partners. Our focus on collaboration and partnership has allowed us to remain at the forefront of innovation and provide the latest and most advanced solutions. Together, our team and our partners are driving success in the industry." - David Bezděka, CEO @Cleevio
New business partnerships
Our focus on business partnerships has allowed us to expand our reach and customer base, as it continues to help us work with a growing number of businesses to provide bespoke solutions. By working with others, we increased our visibility with partnerships and reached a wider audience.
Innovation
Another advantage of building business partnerships is that it has allowed us to become more flexible. By working with other businesses, Cleevio has been able to develop new products and services that are better suited to the needs of its customers. This has allowed us to remain at the forefront of innovation and provide our customers with the latest and most advanced solutions.
We see our evolution from a product company to a company building business partnerships as a step forward. By focusing on building strong relationships with our customers, we increased customer satisfaction and retention and expanded our reach and customer base.
We believe that our approach to business partnerships is a great example of how companies can evolve and adapt to changing market conditions.
If you are interested in learning how business innovations can help your business, do not hesitate to get in touch.
Binance Breakfast: Initiative for a Bright Crypto and Blockchain Future
The first of many Binance Breakfasts took place last week in our Prague offices. The event brought the community together to discuss the future of blockchain and crypto in the Czech Republic. Binance's goal is to educate and empower the community, making the country a hub for innovation in the crypto space.
The first half of the event was a presentation of Binance's vision and values as they shared their insights and perspectives with attendees.
The second half of the breakfast was dedicated to networking and building connections between attendees, fostering an environment of collaboration and community. Binance believes that by working together, the crypto and blockchain space can become a safe and accessible place for everyone.
We at Cleevio are passionate about improving the crypto and blockchain industry in the Czech Republic, so we are happy to have helped make this event a reality. We have ambitions to support Binance with market education, consulting local companies and helping develop local BSC projects.
The first Binance Breakfast was just the beginning, and we hope that participants from the banking, fintech, news and web3 scene left the event feeling inspired and motivated.
The next event will be next month. We will focus on creating an action plan to turn these goals into reality. We can not wait to see the progress made in the Czech Republic. With the collective efforts of the community, the Czech Republic has the potential to become a leader in the crypto and blockchain space, and this Binance Breakfast event was just a first step towards that future.
How the Software Product Development Process Works: A Complete Guide
Software product development is a challenging discipline with a clear goal: to deliver a certain product with the help of information technology in the best possible quality and of the greatest possible benefit. All within a defined time and budget, of course.
Building quality products is impossible without a solid partnership between the client and software vendor. I’ll explain why this aspect is critical and share some practical product development tips in this article.
I’ll also walk you through the stages of the product development process, and give you an opportunity to get actively involved in one of them.
Sounds interesting?
Let’s dive in!
Before You Kick Off The Product Development Process
The very first step of the process is defining a clear product idea and selecting a quality product development company.
Once you know exactly what kind of product you want to develop, you’ll need to team up with an experienced product manager to discuss your vision. Remember that it’s you, the client, who’s at the heart of the process- you have the best understanding of your product and a plan for its future.
The product manager will help you translate your vision into reality. Together, you will start with the preparation phase which can include outlining implementation plans and defining product roadmap.
However, the phase might also reveal potential risks, such as low market demand. A good product manager will let you know how much potential your product has and if there is enough of a niche for it to address.
Pro Tip
Don't be afraid to communicate openly with your product manager!
Remember that you can base your cooperation on an NDA contract that guarantees confidentiality on both sides.
After all, the more information you share with your product manager about your business, product, and plans, the more fruitful the collaboration. The product manager will then have a better understanding of your vision, and will be able to incorporate it into every stage of product development. This, in turn, will make the whole process much more efficient.
For example, if you want to go global with your project, it’s a good idea to plan for multiple languages, even if at the beginning you will be using just one.
If you want mobile development, then according to the plans for the future, you can either go with multi-platform development such as Flutter (in the case of a simple application that will remain in a defined scope in the long term) or with native platforms such as Android or iOS (in the case of a complex application with 3rd party integrations).
All of this proves just how important it is to share your full product vision with the product manager.
How Does Software Development Work In Practice?
As mentioned above, a solid, trusted partnership between the client and a product development company is essential to building a successful product.
Here’s what the software product development process consists of:
- The initial stage - your target audience group, value proposition, and high-level user journey are defined, market analysis is specified, product design is prepared, research is done, and solution concept is defined
- The development stage - the technical preparation of the project starts, the infrastructure is created and the actual development and testing begins
- Pilot stage (recommended) - a pilot for beta users is launched which is later transformed into a live product for everyone
- Maintenance stage - once the project is completed, you need to take care of it, either by maintaining it or developing additional features
The project may involve multiple experts, such as:
- Developers (many platforms) - responsible for software development
- Quality managers / testers - take care of testing and the overall product quality
- Solution architects - define the most appropriate technical solution based on the product requirements
- Analysts - define the foundation of the product and prepares the specifications
- UX/UI designers / graphics - prepare the visualisation of the product with the user experience in mind
- DevOps engineers - if the solution is complex, infrastructure experts need to be involved to take care of the infrastructure, set up proper monitoring, prepare the system for a heavy load, etc.
- Project/product managers - collaborate directly with the client and manages the team to complete the project effectively
Pro Tip
Make sure you work with people with different areas of expertise!
Involving proper experts at the right time will make the product development process much more efficient, and help deliver a better solution.
Every single expert offers a unique, valuable perspective. For example, UX/UI designers don’t just deliver visuals, but produce designs aligned with user behavior which facilitate product adoption.
Quality managers bring a structured, methodical approach to uncover hidden bugs. They provide essential guidance to developers by pointing out error types, locations and circumstances under which they occur. Thanks to their input, developers can fix the errors early on and proceed with building further functionalities. Quality managers’ focus on detail makes them responsible for deploying a stable product to all users.
Speaking from experience, involving the right people at the right time is absolutely essential from a strategic point of view. Each product is different and might require support from different experts. So, make sure you agree with your product manager on what experts you’ll need, even if just for a single consultation.
Software Product Development And Its Stages
We’ve outlined the stages of product development above. Now, let’s dive deeper into each of them and see what exactly they consist of:
1. The Initial Stage
Although critical, the initial phase is often neglected. It includes mapping out of the product definition, specification, design, and solution concept. All of these elements should ideally be based on real user needs verified by a thorough market research.
For the most effective product development, you need a well-written specification covering two levels:
- Business level, where the use cases are defined
- Technical level, where the approach to the development of use cases is defined
If you’re building a mobile application, web solution, game or any other product requiring a user interface, it is also necessary to have a visualisation - at this stage, you’ll need the support of a UX/UI designer who will prepare intuitive designs making your product easy-to-use.
In the initial phase, you should also dedicate time to market research. It will help you define target groups, understand their needs and find correlations between the two. The data you’ll collect will show the real market demand and user expectations.
Unfortunately, due to its cost, market research is often overlooked. Yet, it is essential to the success of your product. It will help you to not just discover and understand your target market, but also better allocate resources, making the product development process more efficient. It can also help you gain advantage by aligning your solution with user expectations better than the competition.
Remember that it is not always necessary to commission the research from a large agency. You can conduct the research yourself using a basic questionnaire that will help you collect the essential information.
If you create a questionnaire yourself, make sure to structure it properly with your target audience in mind. Set a clear number of respondents you want to reach and decide whether you’re going to survey the general public or experts only.
Don’t include too many questions and put the most important ones on top - this way, you’ll get the most critical data while the respondents are still paying attention.
Here’s an example of a questionnaire that one of our partners is currently using for market research - feel free to use it as inspiration, and take the opportunity to actively participate in the initial phase by helping us with your opinion!
The market research insights should be discussed with a solution architect who will recommend the best technical solution based on your brief.
It is key to define the right technical solution at the very beginning of the process, so don’t be afraid to share your vision with the architect. They will present the right approach, and ensure that no major technical changes will need to be made halfway through the project. They will also advise on a security approach that will best help protect your solution.
Pro Tip
Very often, the roles of a graphic designer and a UX/UI designer are misunderstood.
A graphic designer is a visualisation expert who works on user interface. A UX/UI designer focuses both on user interface and user experience.
UX/UI designers combine the in-depth knowledge of design principles with an understanding of user behavior to create a user-friendly experience. They put the elements of your product design into an order that’s aligned with user behavior - as a result, your application will, for example, have appropriately placed buttons or optimized number of clicks leading to a goal completion.
2. The Product Development Stage
The next stage is the actual development of the product that should start with the preparation of the infrastructure.
The preparation includes an efficient setup of the development process that could automate the frequently repeated operations to help save time and move forward faster. It involves setting up the environment/s, infrastructure, security and monitoring in preparation for the actual development.
The development of your product is usually completed in parts. These are delivered in shorter time intervals called sprints. A single sprint starts with planning where tasks for a given time period are defined, and later introduced and developed. Developers meet regularly to address technical issues and potential complications at regular short meetings called standups.
Usually, before the end of a sprint, developers prepare a testing version of the product. The QA team puts it to the test and reports any malfunctions and bugs that the developers need to fix.
The way testing is done depends on the type of your product. In the case of a mobile application, developers have to build code and transform it into an installation package that the tester later downloads, installs, and tests. In the case of web solutions, developers need to deploy the code into the right environment where tests can be conducted.
Depending on the team size, product type, and delivery date, testing can be done in batches, continually, or automatically.
The development stage is one in which you can get involved. After the team completes the development and testing of the demo version of your product, they will present the progress to you. Once this is done, you can examine the features yourself and share feedback with the team. It is best to do product demos on a regular basis to monitor the growth of your product and actively test it yourself.
This collaboration between you and the development team improves the product. You can consistently get an insight into what's been developed and at the same time consult future features or validate best practices with the experts to get your priorities right.
The last stage of testing involves deploying the solution to the right environment where it can be tried by real users.
A sprint ends with a retrospective where the team discusses the ways to improve the solution and collaboration, and plans improvements for the next sprint.
Pro Tip
Testing and deployment are two important areas where automation can be introduced. Doing so usually means a higher effort at the beginning, but subsequently a greater benefit in the future. Manual, repetitive activities can then be solved with one command line or within one click of a button which saves your team’s time and gives them space to focus on other product areas.
On the other hand, your team should only automate where it makes sense. For example, automated deployment, automated login and logout scenarios are usually more efficient.
But because automated tests are performed without human intervention, they cannot replace human evaluation. So if your solution contains functionalities that cannot be accurately described, automating them might not be the best approach.
The best practice is to let DevOps engineers decide what stages of the product development process could be automated and with what tools for optimal results. Other experts will surely find something to automate too, but please always consider added value before introducing automation.
3. The Pilot Stage
I highly recommend starting with a pilot/beta version of your product before full launch.
The opportunity to test the product with several real users and getting their feedback is key to understanding if and how your solution resonates with the target market. The pilot stage also allows you to assess the stability of your product and catch any remaining bugs early, when their impact is still relatively small.
Once your product has been beta tested, it’s time for the big release!
Pro Tip
If you’re not short on time or budget, always plan for the pilot stage. The insights about usability and functionality it provides are invaluable, and can save you a lot of headaches in the long run.
If you don’t have resources to run a pilot, use the data you gathered through testing, and openly communicate to users any shortcomings like missing features. This way, you’ll set proper expectations and minimize the risk of potential complaints.
4. The Maintenance Stage
After deployment into production, the product is available to potential customers. At this point, you can either let it run or continue with developing further functionalities. It is highly recommended to closely monitor the product’s performance and user behavior with analytical and monitoring tools.
Analytical tools help us understand user behavior and therefore better tailor in-demand functionalities - and even marketing campaigns! Monitoring tools, in turn, allow us to keep an eye on the infrastructure and take preventive measures against any crashes, so we can either stop them before they occur or, if they do happen, introduce fixes before the errors affect a large group of users.
In the maintenance phase, you can also do another market research to determine what other functionalities might be a good addition to your product.
Pro Tip
When the product development process comes to an end, two things are often overlooked - documentation and support.
Proper documentation should be an integral part of every solution. As people change teams and projects, the documentation is often the only remaining source of truth about the product - that’s why it should be updated on a regular basis and stored safely.
Support is essential in the post-release phase, as it provides hands-on help with any product issues or upgrades. In the best case scenario, you should have onboarded a dedicated team that is ready to jump in whenever help is needed in the development stage. This minimizes the impact of production issues on your users.
Otherwise, if no further development or support is available once your product is released, you risk running into technical or regulatory issues. The IT world is rapidly evolving - there are new technologies or privacy and security regulations that require your product to be regularly updated. Besides, even the most stable solutions need proper maintenance.
So, always keep in mind the future of your product and the fact that it is going to require good care and upkeep.
The Takeaway
Summing up, when it comes to the product development process, the partnership between the client and the provider is absolutely essential to successfully turning your idea into a product.
Do not underestimate the importance of preparation, and always do proper research, involve UX/UI designers, and openly communicate your vision - all these aspects help to define the right architecture of the solution.
Remember that the developers will deliver your product in smaller bits, so stay in touch with your product manager to regularly monitor progress and ensure that you continue on the path you want to take.
Before you launch, test your product among a narrow group of users to collect feedback and detect any errors. And don’t forget about documentation and support that will help keep your product cataloged and up-to-date.
The best part of the whole product development process is the fact that you can create solutions that save time and money, make processes more efficient, and overall contribute to making our lives easier!
Remote Work Abroad: 5 Things We Learned From Our Portugal Stay
In the flurry of recent job market trends, one, in particular, stands out - remote work. Fueled by employees’ desire for more freedom and work-life balance, working remotely has become a go-to model in many companies around the world. At Cleevio, we took remote work to the next level and went beyond just working from home - we offered our team the opportunity to work from a comfortable villa at the ocean coast of Portugal for 3 whole months!
Here’s what we learned from the experience:
Staying on top of trends is a given at Cleevio.
We keep up-to-date with the latest technologies, industry news, and developments - and it’s no different when it comes to the most recent work trends.
Two years ago, we introduced a hybrid work model. It allowed us to meet and work together in the office while providing space for concentration and deep work at home office.
The model worked great - but we didn’t want to stop there.
That’s why this year we introduced a unique company benefit: the possibility to work from a spacious villa at the Portuguese coast for 3 whole months.
Cleevio in Portugal, a.k.a. Our Workation Setup
So why exactly did you introduce remote work abroad, you might ask.
There were a couple of reasons: we wanted to improve our teams’ work-life balance, enable them to get to know each other better and combine work with play in a new setting.
So when the opportunity arose to rent a 10-people villa in the historic town of Aljezur, Portugal, we jumped right on it.
We decided to rent the villa for 3 months in the winter/spring season (February - April) to escape the cold and dark Czech winter, and enjoy a milder oceanic climate. Plus, the turn of winter and spring is the prime surf season in Portugal, and we definitely wanted to catch some waves!
On top of that, the villa offered great working conditions. Equipped with high speed Internet, spacious common areas, and a large table that fit multiple monitors, the place had everything we needed to be able to work on our projects effectively.
Oh, and did we mention the solar-heated pool and barbecue terrace?
Over the 12 weeks that the villa was available, 33 people from 9 teams visited and stayed on average between 7 and 14 days, wrote hundreds of lines of code together, and made tons of unforgettable memories!
Here’s what our stay in Villa Berenika looked like:
Remote Working Abroad: What We Learned
We’ve learned a lot from our first experience working remotely from abroad.
Here are our top 5 takeaways:
1. We stayed ahead of the trends in an increasingly remote work oriented industry
While remote work is growing increasingly more common, very often it means that employees can simply work from home.
A lot of businesses still don’t allow for remote work from a different country, be it because of their lack of flexibility, office-based culture or legal and tax issues.
That’s why offering the benefit of working from Portugal to our mostly Czechia-based employees set us apart from others. We were able to not just act on, but stay ahead of the trends, and offer our teams a truly unique experience - which is what Cleevio is all about!
2. We improved well-being, work-life balance, and creativity
After over two years of pandemic-triggered restrictions and uncertainty, it’s only natural that a lot of us were feeling strained.
The opportunity to work and have fun together with our teammates in a relaxed, picturesque setting of Aljezur helped address this issue. It provided an environment where we could simply decompress and enjoy the moment - which, in turn, translated to an improved sense of well-being and work-life balance.
The setting had a positive impact on our creativity too. After all, who wouldn’t be bursting with ideas if given the opportunity to work by a pool, at the beach, or on top of a cliff?
3. We appealed to a large pool of talents
Because the possibility of remote work from abroad is a unique benefit, we were able to stand out to potential job candidates.
Throughout the time the villa was available, we promoted it across our social media channels and in active job postings. The content featuring the villa garnered more engagement compared to other posts, and sparked questions from our target hiring audience which proves that the benefit resonated with the right crowd.
4. We enriched company culture and strengthened team relationships
This year was the first one where we introduced the opportunity to work remotely from abroad. And we quickly noticed how much of an impact it had on our culture. It allowed our teams to get to know each other better, experience new things together, and make great memories - all of which strengthened our bonds and friendships.
The Portugal villa became a popular topic at Cleevio. The visitors have started their own Slack channel where they shared activity updates, photos and videos of their stay on a daily basis. There were also lots of stories shared in person in the office which made us all feel a part of this adventure.
5. We got the job done!
Even though we were almost 3,000 kms away from Cleevio’s headquarters in Prague, Czech Republic, we stayed focused on our projects and got the job done.
Yes, we had access to a pool, barbecue terrace, breathtaking views, and perfect surfing waves - but all of this did not distract us from our goals.
We remained organized and aligned on our top priority: helping our clients grow and succeed, no matter where we are.
The Takeaway
Being able to work together from Portugal was a great experience that taught us a lot.
Staying ahead of the remote work trends, appealing to new talents, and enriching our company culture were just some of things that the “Portuguese workation” allowed us to accomplish.
Without this initiative, we wouldn’t have learned as much about our company and each other - which is why we’re glad that we’ve taken this step.
If you’re not sure if your business should go remote in a different country, we would say a strong yes! Test the idea and see how it works for your business, and who knows, maybe it will become a new staple in your organization?
What Is a Soulbound Token?
If I told you that there is going to be something opposite to the NFT called SBT… What would you say?
In May 2022, E. Glen Weyl, Puja Ohlhaver, and Vitalik Buterin published a whitepaper titled Decentralized Society: Finding Web3's Soul. They represent the DeSoc (Decentralized Society) and the Soulbound Token (SBT).
Let's take a look at what a Soulbound Token is.
To explain the mechanics of a Soulbound Token, it is best to compare it to an NFT. If you own an NFT, you can sell it. You can send it to your friend. If you decide you want to buy an NFT, you can just buy it.
The idea behind SBT is that you can't buy it. You can't send it to anyone. Only someone else can issue an SBT to you - Soul accounts exclusively can issue SBTs to each other. The SBT can also be removed by the Soul account that issued it.
Let's summarize what a Soulbound Token is:
It can't be bought.
It cannot be sent to anyone (non-transferability).
It can only be issued by one Soul account to another (the idea is that users should have multiple Soul accounts, for example a medical, work, or education account, etc.).
It is possible to remove a Soulbound Token from the account that issued it.
As with the NFT, it is a blockchain token.
What are the use cases of Soulbound Tokens?
Education degrees
Imagine a world where you have a Soul titles account that would enable anyone to verify if you have actually graduated from a given school. There would be no need to prove your diploma validity. The diploma could not be falsified. It would simply be possible to confirm if a diploma was issued by, for example, Harvard University.
Certificates
After taking a course, you wouldn’t receive a paper certificate, but an SBT would be issued to your Soul account. Driver’s licenses, ID cards, etc. could all actually be SBTs. The state would have its own Soul account from which it would issue SBTs.
Medical records
In the case you change doctors, there would be no more need to forward your medical records from one doctor’s office to another. Instead, all of your medical information would be stored in your Soul health account - not accessible to the public, of course.
Job records
Imagine a digital resume with a confirmation of your job tenure from all the companies you’ve worked for. It would be impossible to introduce changes to the confirmations, so there would be no false information about past employers and experience.
A few more ideas
Birth certificates
Olympic medals
Criminal records
Albert loyalty points 🙂
Your imagination is really the only limitation to where and how such a token could be used.
What happens when you lose your Soul account
The solution could be the so-called social recovery. Simply speaking, it means choosing the people or organizations you trust in your immediate area. These entities will then own the private keys to your Soul account.
In the event that someone steals your account, the entities will be able to recover it. Of course, if they "died out,” you wouldn’t be able to get into the account. It is not a 100% problem free solution.
The Takeaway
If you're interested in SBT, be sure to read the above mentioned whitepaper.
The idea of SBT sounds very interesting to me and even though it might seem sci-fi now, I believe the future is digital and a fully digitalized world is inevitable.
So far, SBT exists as an idea and no technical details have been specified. According to an interview with Vitalik Buterin, SBT should appear at the end of 2022. Will it become another big trend? Who knows. But you definitely can’t go wrong by exploring SBTs now.
What do you think about SBT? Can you think of any other use cases for it? And can you imagine that it will become the norm over time?
Fun fact for the very end: As Vitalik Buterin revealed in one of his tweets, the name Soulbound comes from the game World of Warcraft.
Disclaimer
This article was originally published on David Tilšer ’s LinkedIn profile. Follow David on LinkedIn and Twitter for more blockchain insights and articles.
The Power of Static Dispatch in Swift
Apple has made considerable claims about how fast the Swift programming language is supposed to be, stating e.g. that it is up to 2.6x faster compared to Objective-C.
The details of tests behind those claims were never disclosed and those who verified them themselves found quite opposite results – Swift can be by an order of magnitude slower compared to Objective-C when the code is non-optimized. With optimizations, it is still usually slower, but the difference is not as great.
In his book, iOS and macOS Performance Tuning, the software engineer and researcher Marcel Weiher found that there is a way to make Swift run much faster – but one would lose all the convenience Swift brings while going into pure C or C-based functions.
At Cleevio, we strive to write code running as fast as possible while also maintaining convenience, safety, and readability. Swift is therefore great for most of our use cases (although we also made a C-library for a computation-intensive task for one of our projects), as it meets our needs without having to go pure C-blast.
There are, however, many ways to tackle certain code problems, some more and some less performant. This article focuses on the difference between static and dynamic dispatch. We'll look at it in code and cover the performance implications of its use. We will also discuss the limitations in situations when it’s much less convenient to write the code optimally.
Static Dispatch
Static dispatch describes a situation in which we know what function body will be run in compile time. It’s usually represented by a concrete type or generics.
It could be shown in the following example:
Here, we can be sure that DinnerServer will be the one executing the function and the value we will receive is true. The same applies to classes that are notated with final keyword – they cannot be subclassed, so we are sure during compiling what will be executed.
We can see similar results when we use generics that take advantage of Swift protocols to the limits. Generics are an essential part of making an app with Swift, as the language itself was described to be a protocol-oriented language. Let’s define a protocol first:
Now, we will make a conformance of DinnerServer to this protocol. It is a simple task as the DinnerServer already implements the function.
Let’s say we have a function that takes a Server as an argument and returns the information whether the food is ready. It could be defined like this:
In current implementation, however, we say that it could be any Server. We will get the results, but we don't know what type will do the function and as any Server can be used as variable in the function, we do not even know whether we will receive true. Fortunately, there is a way to tell a compiler what type will be responsible for executing the function using generics.
Here we provide T: Server to the function. This syntax in angle brackets means that we do not expect any Server, but one in particular. The compiler would then create separate versions for every type that we provide in the function in our codebase. So, this will be a very slow margin increase in the size of the compiled application but we can be sure that the execution will be fast, as the compiler would create for us a function that looks like the following:
We can’t see such a function in our codebase, but I present it to you so that you have some mental image of how the compiler can benefit from generics behind the scenes.
We would of course be able to do the same – just declare the isFoodReady(server:) function for every type that conforms to the Server protocol, but that would be an unreasonably wasteful use of our time. Writing it ourselves would also be more error-prone as it would be easy to forget to create it.
Also, it seems feasible when we talk about one function that we would need to create. But what about some more complex code? For example, if we change the Server protocol requirements to include also prepareIngredients and finalizePreparation functions
and then extend our DinnerServer so that it still conforms to the Server protocol:
We can then create a special function for preparing food that takes care of calling both functions like this:
As you saw in the implementation, finalizePreparation for DinnerServer is empty, as our server does not need to do anything more than just prepare the ingredients. But some other Server may need it.
We can now use this in a different function (orderFood) that should crash if the food is not ready after it is supposed to be prepared:
As you can see, if we were to create functions for specific types, the amount of code we would have to write would turn exponential
together with the growth of the number of functions or the types using them.
Generics also help the testability of our code as we can create mocking types just for testing purposes without the need of creating specific functions for them to use.
Dynamic Dispatch
Dynamic dispatch is the exactly opposite situation - we don’t know what function body will execute in the compile time. So, we cannot help the compiler to create the same optimizations as it can when we use generics.
I have already presented how the dynamic dispatch function would look like in our setting. It would be a function that takes any type conforming particular protocol Server:
The result here is that it requires more work during runtime to execute this dynamic isFoodReady. Assembly code from the compiler tells the story. First of all, generic and non-generic functions have differences in their implementation.
As we can see, there is a special call to __swift_project_boxed_opaque_existential_1 in the non-generic function as this is what we have to work with – existentials. Also, in the calling place of those functions, we can see differences.
Calling our generic function is a quite straightforward code:
While when we try to call our generic function, things start to look more messy:
Most notable difference here is that there is an additional call to execute function __swift_destroy_boxed_opaque_existential_1. This additional overhead is what causes the code to run slower. How much slower? That will come in the next section.
The existential is also the name we can see in Swift 5. 6. context that introduced existential any. It’s just a way of Swift that tries to force us to declare any before any protocol type that we want in a function (for example). This approach should make us more aware of using dynamic dispatch. We can expect to get a warning or error in the future versions of Swift if we don’t use the word any in our codebase. The above-mentioned code would look like this:
Just that simple. All it takes is to add the word any.
Another example of dynamic dispatch would be a class that is not defined as final. The compiler cannot be sure what class is provided to the function – is it the class that is defined in the function, or one that inherits from it? When the class is declared as final, inheritance is no longer possible and the compiler can then create other optimizations. The same applies to specific functions or variables. If declared as final, they cannot be overridden and the compiler doesn’t have to use dynamic dispatch.
Why would anyone use dynamic dispatch then? We will look into that in the section titled Limitations where I’ll show you examples where static dispatch may be – at least – inconvenient to use in some situations.
The Results
In general, we can expect dynamic dispatch to be slower than the same functions that use static dispatch. Those are indeed the results. The question here would be – at what cost? And the answer differs based on whether we are talking about generics or final classes.
Measuring
I will present the results of using static versus dynamic dispatch. For the measurements I created a library that takes functions defined in a type called MeasureFunc (containing function and its name) and the number of repetitions.
It is very straightforward – it repeatedly runs all functions and then appends the result to a dictionary, which is then sorted and the results are then handled – the library can print them as well as save the results as CSV, for example. The average time of each function is then calculated as a mean of the runs, using CAMediaCurrentTime which is much faster to create than Date and should therefore be more precise.
I will show you the results in relation to the fastest function – so, for example, if one function would take 1 minute and the other would take 2 minutes, the first function would be shown as index 100% and the second as 200% – as it takes 2 times more time to execute it.
The code was compiled with release optimisations, but we can expect it to be much slower without them.
I use TestingClass function for all tests that I will present to you, which is declared like this:
It conforms to a protocol Testable, that has only one requirement – to support init without parameters. I’ve used UUIDs because they take some time to generate even though they are value types.
Appending Class to an Array
Code reuse is one of the great ideas of modern programming. We don’t want to repeat ourselves and we want to change, if possible, only one line in one place, instead of one line in multiple places.
First example when I tested statically and dynamically dispatched code was with adding our TestingClass to an Array. Yes, it would be faster to add it to a ContiguousArray, but – let’s be honest here - nobody uses that (and even I do it only sometimes).
The code to add to an array is very simple for generic type, as it is declared by taking one item and appending it 100 000 times.
Using a dynamically dispatched function changes the code, only that we use Testable directly instead of T. With Swift 5.6. we would use any Testable.
The results are not surprising – it is much faster with generic function. How much faster? By about 62%.
Appending Class to an Array with Initialisation
Sometimes, we may need to not only append one specific item to an array repeatedly, but also create it based on some data and create a model from it. The initialization itself would therefore take some time. And even though the appending would be much faster (as we said before, by 62%), the total time of the function should be much lower as the init time should be the same.
The functions are very similar to just append to an Array, with the only difference being that we initialize it when we append it. The generic function looks like this:
While dynamically dispatched looks like this:
The results support what we expected, using dynamic dispatch is 7% slower than using generics, because significant time is used on the creation of the class. If it wasn't a class or didn’t have 5 variables, the dynamic dispatch would be relatively slower (but never more than the 62% that we showed in the previous test).
Class Recursion
We’re not going to talk about any recursion algorithms (that are usually not even the most performant way to perform tasks). Here, I will show you a very simple code of some class that takes a parameter and then in turn hands over that parameter. The usage of such may be some dependency injection – like in coordinators or possibly in some SwiftUI ViewModel handling.
Recursion using static dispatch looks like the following. As you can see, we start with some item that conforms to protocol Testable and then recurse 5 times (starting from 0).
Dynamic dispatch looks very similar, the only difference is that we declare storage to be Testable instead of Generic T.
Again, static dispatch is much faster, in this case by 67%.
Final Classes
I found only a minute difference when I declared classes as final. But it is definitely a part of Apple’s optimization tips you can find on their Github . I haven’t focused much on benchmarking final classes in tests for this article, but we can see that we can gain a little when we use final classes to improve not only our runtime, but also compilation times.
I recommend this great benchmark from Swift forums done by Jon_Shier that shows that declaring classes as final can bring us around 1% of compilation speed. Not bad, right? And since using generics takes a little longer, we could gain what we lost. At least partially.
Limitations
Our iOS architecture is based on MVVM-C. We first provide dependencies to Coordinator, which is then responsible for creating ViewModel, ViewController, and then attaching ViewModel to ViewController (on View, depending on whether it is newer app that uses SwiftUI or older one built purely on UIKit).
It was fairly easy to start taking advantage of static dispatch in coordinators – As instead of:
We now use generics declared as:
That code is even shorter as it’s not necessary to create typealias for Dependencies to use later in functions of coordinator, so that whenever we would need to change the dependencies, we would change it only in one place.
One of the tidbits here is that whenever you try to go for this change of taking generics instead of any existential, you would have to change it in all places – or at least start with some root coordinator and then continue down with the flow. To showcase this, let us declare a protocol Developer:
and a type conforming to it:
This code compiles:
But the following doesn’t. It is due to generics expecting a specific type – and the compiler cannot make a specific type out of existential.
But it's not as easy when we work with ViewModels and ViewControllers. If ViewModel were generic, it would have to be specifically declared in ViewController using Generics as well as in ViewModel itself (you can see it in the following example in the angle brackets where we have to duplicate the code). We would then have to declare what protocols ViewModel conforms to in ViewController, which would then be little harder to refactor when needed.
It’s also not possible to extend a class to be a delegate for objective-c API when that class uses generics, as can be seen in the following example where we’re getting the error „Conformance of generic class ViewController<T> to @objc protocol UITableViewDelegate cannot be in an extension). Yes, it would be possible to solve through another object that would serve as that delegate, but that would require significant changes.
Generics can also become highly complex when we want to have default arguments for Generic dependencies. It’s very easily seen as the context of SwiftUI, but applies to any context that uses Generics. To showcase this, let us create a very easy ContainerView that has two attributes – content and its overlay.
So, what if we want to create some default arguments for it? We would have to extend ContainerView with them. Let’s say that we want to have a default argument for content to be Text and for overlay to be EmptyView.
For Content it would look like this. You can see where Content == Textwhere we declare that this is an extension that applies only when the Content is Text. And also in the init, there is Text with a localized string of „Placeholder“). So whenever you would use ContainerView(overlay:) init, it would show the Text with Placeholder string.
And for Overlay to be by default EmptyView like this. The result is that we can now omit the overlay parameter:
But, what if we wanted to have an init that has two default arguments, Text and EmptyView? We would then have to declare another extension like this. This approach allows us to combine previous two extensions and declare the ContainerView only as ContainerView() without the need to specify any argument:
But what if we had some other arguments that we would want to default? As you can see, the complexity would be exponential here. For two default arguments, we need 3 extensions. For 3, we would need 7 arguments. Here, the discussion should also be about the maintenance, whether a pure Generic approach is suitable from the beginning when the API or app architecture is not finished yet, and might be changed significantly in the future.
It’s worth adding that there’s just no way around it in some places if we decide to use generics. Apple uses exactly the same for their SwiftUI elements. While we can’t see the actual implementation, we can see the headers and names of the functions in the documentation.
There are two arguments for SwiftUI.Label – for Title and Icon. And as Apple wants users to be able to use some convenience inits, they also have two additional extensions (as Apple doesn’t have any default parameters here) containing convenience initializers. For example, in the first extension where Title is Text, there are convenience inits to take String, LocalizedStringKey, as well as String for image or SF Image. As you can see, there is no way to initialize a Label with some View but also a String of an image – that would require them to create another extension. The same applies to the case where we would have Title as a String but Icon being some View. Making API is just hard and you either provide all the convenience inits or make your life easier.
These limitations are definitely not finite. Depending on the context, there may be ways to overcome this. If you have any good examples of limitations or how to solve them, please let me know!
Swift 5.7
Apple has brought many great additions to Swift 5.7. They significantly improved how we use generics and deprecated some of the limitations I mentioned before.
For example, we defined a following function in the first chapter of this article:
In Swift 5.7., all it takes is to write it as following:
The angle-bracket approach that might have frightened potential users of generics is gone – with the use of the same some keyword we know from SwiftUI, now also in a parameter of a function.
I also showed how hard it is to define default arguments for parameters in SwiftUI. Not anymore. For our use case, we needed 4 inits. All it takes in Swift 5.7 is to have one of them.
And most interestingly, it is now possible to use something people from Apple call existential unboxing. What does it mean? We can now use an existential in a generic function. I think it sufficiently illustrates following example:
As you can see, you can have an existential any type that can call a generic function, which – as it always could – then calls an existential function again. This (with the other additions) is the reason why my presentation for team about generics in Swift 5.7. and how to improve our code with what’s to come was called Big Year For Generics.
From Swift 5.7. we can gradually adopt using generics in code, as it is not necessary now to update the whole coordinator hierarchy if we want to start using generics. We can start with just one and continue along the way.
Swift 5.7. therefore greatly improves what we already had, while the previous code still compiles. Just beware of switching to Xcode 14 too soon, it is by no means stable and has serious issues as of the time of writing the article.
Final Thoughts
So, what to take from this article? Try to use the power of static dispatch whenever possible. Swift is a great language for the coder, although it is very compiler-intensive. And the compiler needs help from us, which we can achieve by using generics, final classes, or even some private and other declarations.
But be careful about over-optimizing. As I showed in the Limitations section, there are situations in which it would require significant refactorings of current code – and may make it harder if we wanted to refactor that code in the future. Always think about feasibility as well, as you may burn a lot of your – or the client’s– time to optimize some code. And even though there are significant performance implications, our phones, computers, and tablets are so powerful nowadays that they may not be noticeable to the user. It’s great to optimize whenever possible, as it may even save some battery life, but don’t overdo it.
Finally, I wanted to share some pieces of practical advice. I believe that your code that uses existentials without any will get you a warning from the Swift compiler in the future – and when it does, think about making it generic instead of inserting any in there. When it comes to classes, you can for example use the following SwiftLint rule for getting a warning if your code contains a class not declared as final (as you can always disable the rule for classes that are to be subclassed). You can of course change it however you like, in our case, we use it only for Managers, Services, and Coordinators.
GoodTrust: Navigating the Digital Legacy Landscape
The founder of GoodTrust Rikard Steiber found out how difficult it can be to manage someone’s estate after friends died during the pandemic. He witnessed the family members struggle to complete tasks such as closing social media accounts, securing photos, and dealing with financial services accounts. This was the main reason why he decided to start a GoodTrust - a digital legacy management company that can handle your digital affairs after you die. That might sound pretty dark, but since we’re all going to die someday, we probably don’t want to leave our family and loved ones with a big mess to fix.
It’s actually pretty difficult to locate all the digital accounts and then also to close them down or actually extract content or money from them. If you think about all your emails, documents, subscription services, your banks, your insurance, your social media — that’s a lot of digital stuff, which means that if someone dies and hasn’t thought about it, you’re not going to be able to access most of these things.
Some of the tasks that GoodTrust can take care of include: setting up a memorial page on your Facebook account; stopping subscriptions that charge you monthly fees, like Netflix and Spotify; and closing down accounts that are no longer needed, such as LinkedIn or Instagram. It can also discreetly close accounts that you might never want to be discovered. And it can rescue the family photos and videos you want to be saved for posterity.
What makes GoodTrust different?
Compared to other similar services, GoodTrust handles all of the legal, financial, and emotional legacies heirs typically have to deal with. GoodTrust plans start at $40, and the service can lockdown more than 100 sites. In more expensive plans, it can even get court orders to gain access to accounts that require such measures. The company can also handle Facebook memorialization for first responders and their families at no cost. In a 2020 survey, GoodTrust found 90% of U.S. adults do not know what happens to their digital assets (emails, photos, social media, online banking, sites/passwords) when they die, and 84% said they would use a secure online service to transition those assets when they die.
150,000 people die every day globally and while each tragedy affects loved ones on a personal level, it also means the number of deceased people on the internet is growing rapidly.
Shuttering an account on someone else’s behalf is made more complicated by legal requirements that vary from site to site. Retrieving personal photos from a loved one’s account on Google, Apple, or Facebook requires expert legal knowledge and a court order. The companies aren’t trying to make a survivor’s life difficult but they simply can’t turn over an account to someone claiming to be a survivor when the “dead” person was still alive.
So why should you care about your digital assets after you die?
When your accounts lie idle, your information is at risk of being lost forever or even hacked, If you don’t do anything, then you can’t really control what’s being posted on Facebook such as some inappropriate pictures, or you get inappropriate birthday greetings, which no one really wants to receive after all of this.
This of course, becomes even more difficult when money is involved, such as a Coinbase account for cryptocurrency or a Robin Hood stock-trading account. Not only do you have to prove the owner has died, but you also have to prove you are the rightful heir which can get tricky.
The issue of trusting a company with secrets and passwords is a pretty big one. GoodTrust doesn’t need the deceased’s user password to memorialize Facebook, stop Netflix subscriptions, delete LinkedIn accounts, extract photos from Google Photos, and so on.
The way it works is that customers give GoodTrust power of attorney and all the documentation required by the websites. GoodTrust does not have access to customer accounts, nor does it see any of its content.
The company tries to handle a lot of this through automation but in some difficult cases, GoodTrust offers a “white glove service,” and the company will add more such services over time. Things like a password manager may capture all of your sites and passwords, but you probably don’t want to hand this over to someone else.
The best way to protect the digital identity of a deceased loved one is to take control of their digital accounts (or delete them), as this prevents hackers from taking over forgotten accounts or impersonating a user for scams.
The People Behind GoodTrust
GoodTrust’s founding team includes serial entrepreneur Markus Thorsveldt (chief technology officer), Olivia Gorajewski (chief operating officer), and Christian Lagerling (chief financial officer). Former Google employees include Scott Levitan and Daniel Sieberg. Advisers include Kallayil and Google ads research engineering VP Tony Fagan. Steiber was previously president of Viveport and senior vice president of virtual reality at HTC, which makes the Vive VR headsets.
10 Usability Principles Every Designer Should Know
What are the Usability Principles?
25 years ago, Jakob Nielsen described the 10 general usability principles for interaction design. These principles were developed based on years of experience in the field of usability engineering and they’ve become rules of thumb for human-computer interaction.
Today, they are just as relevant as they were then. These usability principles can help to save development teams considerable amounts of time during early usability testing so that they can direct their attention to more complex design challenges. In addition, it’s also worth it to use them as a checklist when designing a new product or a feature.
Also, do not forget to check out Cleevio's latest design articles: 7 Tips For A Great UX Survey Result, Understand Your Customer Needs With The Kano Model.
Visibility of System Status
People love to keep things under control, and only then they can feel secure. From the evolutionary point of view, a need for safety and for physiological needs (like food, sleep, and sex) helped us to survive. The sense of control can be evoked by providing information about the system status and feedback after every interaction.
Take a look at your smartphone. Right after the screen lights up, it informs you about its battery, a wifi connection, received messages, missed calls, and much more. Imagine how insecure you would feel if this information were missing. By utilizing signs, icons, and indicators, the system communicates its status and helps user make better, more informed decisions.
When people interact with any system it should always provide immediate feedback on the interaction. Each of us have been burned by a bad experience in the past, which has left us sceptical and suspicious a priori. Just a visual sign like the change of the button’s colour, a loading spinner or an icon animation can help the user to understand what’s going on and prevent them from other unnecessary interactions.
The Match Between The System And The Real World
People are approaching every new system with a mental model in mind. In other words, people presume how the system could work based on their experience with other systems that are similar. By using language that they are familiar with, you can help users overcome the initial awkwardness.
An extreme example is a skeuomorphism design, which transfers all details of real world objects into the software. At the beginning of smartphone adoption, it helped people to learn how to use their new companions through the aesthetics and processes they were familiar with before.
Great examples of real-world matching icons
Even in today’s minimalistic world, dozens of design clues persisted from that era: apps like the compass or the calculator, or design components like folders, toggles, or lock icons. In addition, language and concepts from the real world help users to easily understand the system. That’s why the app for storing cards is called “Wallet,” we’re using “Bookmarks” for saving our favorite websites, we use the “Trash Can” to remove old files, or the “Shopping cart” while shopping online.
User Control And Freedom
People often interact with the system in a hurry, and often times, they’re not even fully concentrated. This results in things like misclicks or other accidents which might be frustrating. Imagine a situation that involves something like accidental deletion of an important file or posting a grammatical mistake on your company’s social media; every system should have a clearly marked “emergency exit” mechanism, that provides users with an easy way back after they find themselves in an unwanted state.
“Every system should have a clear emergency exit.”
An appropriate emergency exit can be something as simple as an arrow back (e.g. in a browser), a trash bin, which protects us from accidental deletion, or the “undo” button, which lets the user to revert the last action. All of these examples demonstrate systems which don’t let users down when they make a mistake, and instead, they allow the user to fix it.
Consistency And Standards
Have you ever noticed that the copy-paste functionality works exactly the same, no matter what app you’re using? What about the fact that you can get on your home screen by simply swiping up from the bottom edge? These are just two usability patterns that Apple uses to make their system consistent and predictable for users. A comprehensible system should never confuse users by using different words, visuals, or actions for the same concepts.
“Don't forget that people spend 90% of their time interacting with other apps.”
A good starting point for a consistent design system of your mobile app are both Apple‘s Human Interface Guidelines and Google‘s Material Design Guidelines. They present a solid foundation that describes important design components with many examples. While designing your new app, never forget that people spend 90% of their time interacting with other apps, therefore using best practices and common patterns will eventually result in a much better overall experience. Consistency is one of the strongest contributors to usability.
Error Prevention
Based on Don Norman’s book The Design of Everyday Things, there are two kinds of errors created by interaction with a user interface: slips and mistakes.
Slips happen when the user tends to do an action, but due to low attention, performs another one (e.g. when performing well known task). The strategy to prevent users from experiencing a slip is to minimize the chance of it occuring by guiding them only through the safe areas. Use constraints that don’t allow a user to set a wrong value (e.g. when you expect a number, don’t allow to write the letters), suggest the most common options to make choosing easy for users (e.g. while searching), or use confirmation dialogs before destructive actions.
Smart slip prevention in the Gmail web app. Unfortunately, the mobile app lacks this feature.
Mistakes are often caused by a user’s incorrect mental model of how the system works. In this case, the user misunderstands the communication and consciously performs an action which leads to a different result than they intended. These kind of errors don’t often come with an easy fix, and they should be revealed during the user testing phase. Use clear communication and a consistent design system to prevent mistakes.
Recognition Rather Than Recall
There are two types of memory retrieval: recognition and recall [5]. The recognition happens when you easily recognize a person or an object that you’re familiar with. It is a very shallow form of retrieval from memory and it doesn’t require any work. The recall happens when you have to find rarely used information in your memory (names, years, details, etc.). To recall information, people have to activate more memory chunks. Therefore, the recall process is a deeper retrieval and requires more work. (That’s why multiple-choice questions in tests are much easier to answer than open-answer questions.)
A good user interface doesn’t require the user to recall frequently. Instead, it offers all options and information required to make a choice. It’s much easier to quickly scan through icons or a text menu and select a coveted feature than trying to recall it from your memory and then write it into some terminal-like text interface. Give users clues for remembering information, and provide an icon next to the feature name or use a specific color for related functions. Well designed information architecture also helps with a hunt for information.
Users who are not familiar with the syntax of terminal commands can’t perform as easy operation as opening or deleting the file.
Flexibility And Efficiency of Use
Every user is unique; each have their own different needs and skills. Equally, every task is unique and requires different controllers.
Declutter the screen and make the app easier to navigate. The app should always display only relevant UI elements and commands. Look at apps like Apple Pages or G-Drive apps; when you’re writing a document, you see just a few controllers related to text editing. But when you decide to add an extra chart, a whole new palette of features specifically curated to help you complete this task appears.
Don’t forget about professionals and advanced users in general! A new user who is entering a learning curve [6] will always have different needs than the professional who’s using it a few hours every day. Power users might appreciate advanced options, shortcuts, or even extendability and customization of the app’s interface. Power users need to save their time and perform tasks quickly, but precisely and reliably too. A good user interface should offer appropriate functionality to both inexperienced and experienced users.
Advanced Photoshop Shortcuts [7]
Aesthetic And Minimalististic Design
Minimalism is not only a fashion of last few years, but it certainly is a lasting trend with the aim to reduce the description of a subject just to its necessary elements. It has many applications in art, music, and literature. Minimalism helps users to quickly access important information and come to the result quickly.
”Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.” – Antoine de Saint-Exupery
To let the remaining content stand out, you can use nothing but whitespace. It helps to increase content legibility, it highlights call to actions, and creates a balanced and pleasant look [8]. A minimal design uses only the necessary colours to support the visual hierarchy. Think about the purpose and meaning of each color. Use it consistently.
Help Users Recognize, Diagnose, And Recover From Errors
Errors and issues of any kind can be frustrating for the user. Especially when they’re poorly designed and communicated. Whether we want it or not, users always tend to get themselves into situations that they need to find a way out of. To minimize the frustration, we should put as much effort into designing error experience as we put into the rest of the system.
A bad example of the error message which is neither clear or useful for the user.
Every error message should be as explicit and precise as possible. Nobody wants to read vague messages like “something went wrong.” State what happened in a readable human language. Messages like “Class error 372,” are just as absurd. Give the user some constructive advice on what to do next. Propose the solution or direct the user to a customer support employee who can handle the situation. The last rule of good error messages is politeness. Never blame the user or imply that they’re stupid.
A great example of the error message which immediately suggests the next steps.
Help And Documentation
Every app should strive to be perfectly usable without any documentation, but as we mentioned before, every user has different skills and different levels of knowledge, and what is easy for 90% of your users might be difficult for the remaining 10%. Well written documentation, FAQs, and tutorials might be crucial for retaining the stunned user.
Documentation should be well structured, written in a human language, and minimalist. Sometimes, users don‘t need a whole lot of documentation; a simple coachmark showing how the new feature works or a brief onboarding guide that explains the basics is enough. Apps like Trello, Slack, and Duolingo are doing a great job at onboarding their users.
Conclusion
Here at Cleevio, we've designed over 120 digital products and those 10 usability principles have been used in the design of each of our products. Also, Cleevio has recently been announced a Winner of Mobile UX Awards. So if you are wondering about designing a new product, feel free to reach out to us as we are happy to offer you a free consultation.