I've found myself in many situations where I am either reviewing my own or someone else's code and I keep thinking that I am probably not considering something that I should. I also try to help my company develop better software and there's probably a lot more stuff we could do but I can't think of any. These events led me to investigate what makes software great. I organized all my knowledge and all I could find about software qualities in one place that I could regularly check to ensure I'm not forgetting something. This is that place.
In this post I will try to explain what each quality is about. These are the qualities I'm most familiar with as a professional software engineer. My experience mainly consists on developing and maintaining a private equity marketplace, accessible via web applications. In total I've got 11 qualities. They are, in no particular order: correctness, availability, efficiency, security, maintainability, adaptability, usability, usefulness, stability, community and support, and pricing. Depending on where you consult, each standards commitee has its own list of qualities, all of which I found too narrow as they mainly focused on requirements specification and implementation and didn't consider qualities such as usefulness, pricing, and others, which I find essential for software to be considered good. If you want to learn more about what others think of this matter, I found the Wikipedia article a good starting point.
There's an infinite amount of things one can do to improve each quality. There's so much that large organizations tend to hire severals specialists to focus on just one. In this post I will also try to provide a small list of suggestions for each quality, highly based on my experience, which should let you get started. In case you don't understand the suggestion I recommend searching for it in the Internet.
How can you improve any quality?
There is a small list of suggestions I find applicable to improve any quality, which are:
- Build, measure, learn, prioritize, repeat.
- Foster critical thinking.
- Prefer data-driven decisions.
- Don't wait for problems to happen. Proactively try to find them.
- Split tasks and responsibilities among different people.
Table of contents
- Correctness
- Availability
- Efficiency
- Security
- Maintainability
- Adaptability
- Usability
- Usefulness
- Stability
- Community and Support
- Pricing
1. Correctness
Correctness means that the software works as expected by the software makers. It's the first thing engineers try to do, which is "Make it work". I personally believe that behaviors that were not expected and also not supported cannot be considered bugs, otherwise everything is a bug.
How can you make your software less buggy?
Re-use
- Prefer buying or re-using (open-source or private) software instead of building your own.
Test
- Test your software against a set of automated and manual tests.
- Perform demos with stakeholders.
Misunderstandings
- Understand as much as possible what are the requirements and the reasons why you need that new feature.
- Really try to understand how a system works and be able to explain the impact of your changes.
- Challenge complex features.
- Remove features as much as possible.
Detect and don't forget about bugs
- Track your errors and bugs (using Airbrake, Github issues, JIRA).
- Maintain a communication channel where users can report bugs.
- Add ability to use the software exactly like your users.
- Monitor health metrics.
- Monitor user behavior.
- Implement data integrity checks specifically adapted to your data models.
Avoid bugs with automation
- Use statically-typed programming languages.
- Use programming languages with garbage collection.
Concurrency
- Understand how concurrency affects your software. Your code might have race conditions.
- Prefer using functions and immutable data structures over mutating shared state.
Failures (machine, network)
- Make your code idempotent.
- Use database transactions if you use a relational database.
- Check if your dependencies can receive idempotency keys like Stripe.
Let others help you
- Implement a code review process.
- Implement and follow coding conventions.
- Make most decisions with other team members before you start coding.
- Regularly become up-to-date with best practices.
2. Availability
Availability means that the software is available to be used. This quality is related with resilience, which is the ability to handle failures, and with recoverability or self-healing, which is the ability to recover from failures.
How can you make your software more available?
Re-use
- Prefer paying for a service instead of managing your own.
Avoid failures
- Use a job system that allows you to run code in the background and where jobs are retried when they fail.
- Test how your software performs under stress.
- Simulate failures (using tools like Chaos Monkey).
- Do post-mortems when there are incidents.
- Understand how your software is deployed. Your code might be causing down-time.
- Use infrastructure in the cloud over on-premise. It enables auto-scaling.
Detect failures
- Monitor your availability (e.g. use Datadog, Pingdom, status pages of 3rd party dependencies).
- Monitor health metrics (both hardware-wise and product-wise).
Recover from failures
- Implement a reliability incident response process.
- Regularly perform data backups.
- Blue-green deployments.
- Phased rollouts.
Let others help you
- Regularly become up-to-date with best practices.
3. Efficiency
Software is said to be efficient when it requires as few resources as possible, where resources can be time (CPU, GPU, people's), memory space, disk space, energy, money, network bandwidth, and others. Users typically notice more when software is slow (time). In most cases, the reason why software is slow is because of how it works (code) and not due to lack of resources.
How can you make your software more efficient?
Re-use
- Prefer buying or re-using (open-source or private) software instead of building your own.
Prevent and detect
- Compare the current performance with the theoretical maximum.
- Think about performance when designing your software.
- Test how your software performs under stress.
- Monitor performance (e.g. using profilers or services like Datadog) in both development and production.
- Understand how the code works.
- Understand how systems are interacting.
- Understand the platform that is running the software (e.g. web browser, hardware).
- Use tools that suggest performance improvements (e.g. N+1 database query detectors).
- Create performance tests that run automatically. This will allow you to detect regressions.
Improve
- Design your code to handle multiple items at a time instead of a single item at a time.
- Use database indexes if you use a relational database.
- Consider de-normalizing data.
- Read Data-Oriented Design.
Let others help you
- Implement a code review process.
- Create a performance culture.
4. Security
Security means how likely attackers might breach the software, i.e., interrupt its activity, gain access to sensitive information, change its behavior to achieve some other goal (e.g. mine Bitcoin). Software can never be completely secure. Given enough time and money, any software can be hacked.
How can you make your software more secure?
Re-use
- Prefer buying or re-using (open-source or private) software instead of building your own. Just because your software doesn't have public CVEs doesn't mean it's more secure.
Detect
- Run security vulnerability scanners (e.g. Brakeman).
- Regularly check security vulnerability databases or directly track CVEs.
- Use tools that monitor logs and detect suspicious behavior.
- Use tools that monitor traffic and detect suspicious behavior.
- Create threat models for your systems and processes (e.g. following STRIDE).
Prevent
- Keep software dependencies and tooling up-to-date.
- Follow the strategy of defense-in-depth.
- Follow the principle of least privilege.
- Don't assume or trust. Instead, verify.
- Keep data encrypted as much as possible.
- Data can be both a liability and an asset. Regularly check if it still makes sense to keep that type of data.
Test
- Try to attack your software.
- Hire penetration testing services to find security vulnerabilities in your software.
Let others help you
- Learn more about existing types of security vulnerabilities.
- Implement a code review process.
- Regularly become up-to-date with best practices.
- Compare your systems architecture with best practices (like AWS Well-Architected Framework).
5. Maintainability
Maintainable software means it's easier to understand and to change (only small things like bug fixing, not including adding new features). It's also easy to build, run, test, install and update.
How can you make your software more maintainable?
Standardize
- Set and follow coding conventions.
- Use tooling to standardize code instead of making up (e.g. Rails, Rubocop, Ruby).
- Automate continuous integration (CI) and continuous deployment (CD).
Simplify
- Understand as much as possible what are the requirements and the reasons why you need that new feature.
- Remove features as much as possible.
- Consider multiple designs and their trade-offs before settling with a solution.
- Refactor code to better reflect the new knowledge.
- Maximize cohesion and minimize coupling. In other words, put together what is related, and make independent what can be independent.
- Prefer using functions and immutable data structures over mutating shared state.
- Minimize number of systems.
- Prefer homogeneus over heterogeneous. In other words, keep the tech stack small.
- Use tools to detect unused code.
Document
- Use version control software.
- Document important decisions.
- Document projects. The code didn't appear there just because.
- Document systems and their interactions.
- Document APIs.
Let others help you
- Implement a code review process.
- Maximize knowledge sharing between team members and across teams.
- Regularly perform retrospectives where you and your team can discuss of what went well, what went wrong and how it can be improved.
Monitor
- Monitor which systems require more time to be changed.
6. Adaptability
Adaptability means that the software easily adapts to new situations such as new features, new platforms, new hardware, new user interfaces, etc. For me adaptability and maintainability are two different qualities of software because, first, it's possible to have software that is maintainable but is not adaptable, and second, it's very hard to make software adaptable. The main reason that seems to make software really hard to adapt is that humans are complicated, and the software that has to model or interact with stuff created by humans is even more complicated.
How can you make your software more adaptable?
Simplify
- Maximize cohesion and minimize coupling. In other words, put together what is related, and make independent what can be independent.
- Challenge complex features.
- Remove features as much as possible.
Test
- Test your software against a set of automated tests. The higher the overall test coverage, the higher the likelihood you won't introduce a regression when making a big change.
7. Usability
Usability is related with user interfaces. When a software is highly usable, people call it intuitive. It's software that is as simple as possible to use. It's easy to understand. It follows appropriate language for its goals. It's consistent. It's pretty. It follows standard conventions. It uses the appropriate communication channels to communicate with users depending on the message.
How can you make your software more usable?
Re-use
- Prefer buying or re-using (open-source or private) a UI component library instead of building your own. If you build one, document it.
User feedback
- Test the software with users.
- Maintain a communication channel (e.g. email, Intercom) to receive user feedback.
- Gather quantitative and qualitative data from users. Better if you can get quantitative data from users without explicit user input.
Research
- Get to know the different types of users of the software (e.g. for an investing platform that might be investors, entrepreneurs, staff).
- Understand the users' needs you're trying to fulfill.
- Prototype multiple designs before settling on one.
- Understand how the software works and the data it manipulates. Reading documentation or talking with engineers will most certainly help.
- Learn more about how users interact with the platform where your software runs.
- Document user journeys.
Execution
- Participate in demos to ensure your designs are being correctly implemented.
Simplify
- Remove features as much as possible.
- Follow the conventions of the platform the software is running on.
Let others help you
- Understand what are the industry standard user interfaces for your type of software.
- Learn more about how to best use each communication channel.
- Perform demos with stakeholders.
8. Usefulness
Software is said to be useful when it solves some users' needs. The more needs it solves the more useful it is. It's also more useful when it can be used in different situations. For example, a text editor is more useful if it allows to edit more types of text.
How can you make your software more useful?
User feedback
- Maintain a communication channel (e.g. email, Intercom) to receive user feedback.
- Maintain a forum where users can talk with the team and with other users about the software.
- Gather quantitative and qualitative data from users. Better if you can get quantitative data from users without explicit user input.
Research
- Get to know the different types of users of the software (e.g. for an investing platform that might be investors, entrepreneurs, staff).
- Document users needs.
- Understand if the software is solving users' needs. This requires understanding both the software as well as users' needs.
- Run ideation sessions to generate new ideas.
- Understand how network effects benefit your software.
- Understand how quantity of data benefits your software.
Prioritization
- Prioritize needs and problems that need to be addressed.
Complexity
- Explain users needs to other people involved in developing the software.
- Participate in demos to ensure your features are being correctly implemented.
- Think about the trade-offs of every feature.
Announcements
- Provide documentation for the software so users can discover new solutions.
- Publish release notes.
9. Stability
Software is stable when it minimizes breaking changes. This is specially applicable to software used by other software engineers, but it also applies to GUIs.
What can you do to make your software more stable?
- Think about the trade-offs of introducing that breaking change.
- Forbid breaking changes, like Linux does.
- Introduce versioning.
- Phased rollouts.
10. Community and Support
Software is said to be supportive when it has at least one communication channel where users can ask for help and have their questions and concerns cleared. It's the team of humans that is always ready to help users and to be their voice when talking with the remaining team members.
How can you make your software more supportive?
- Empathise with the user.
- Act upon user feedback.
- Get to know the team to better understand who to ask for help when you don't know how to answer a user.
- Create FAQs.
- Maintain a forum where users can talk with the team and with other users about the software.
- Monitor how long it takes to help a user.
- Monitor how many users have been helped over time.
11. Pricing
Software is said to have a good price when users are happy to pay for that price to be able to use your software. Nowadays a lot of software isn't paid directly but indirectly (as part of a service). I still think it makes sense to include pricing as a quality of software, even if that pricing includes more than paying for the software.
How can you improve your pricing?
- Learn more about pricing models.
- Identify how much the alternatives to your softwares cost.
- Test multiple prices.
- Be aware of how much your software costs to you and question if each of its costs are worth the benefits. Involve your team members as they might have more context of why you have some cost and also might be aware of cheaper solutions to those needs.
- Raise money from investors. It will allow you to support losses longer, hopefully until you have more users that make the cost per user equation favourable.