Categories
Software | May 10, 2023

Best Practices For Building High-Quality Software

Introduction

Software has revolutionized the way we live, permeating every aspect of our daily routines. It lets us communicate with family, friends, and coworkers, eases our daily tasks, and is useful for entertainment, like watching movies, listening to music, and playing games. Online shopping is very popular nowadays, and even if we’re not buying online, we research products on the web before going to the physical store. Software and the internet also allows us to take better care of our mental and physical health and learn new things constantly.

Software solves many of our needs, and as software engineers, it is our mission to provide the best user experience and add value to their lives through our apps. Although there is no magic formula on how to build high-quality software, I’ll give you some tips to take into account in order to achieve that.

What does high-quality software mean?

Let’s begin by defining what a piece of software needs to be considered high-quality:

  • It does what it’s intended to do and does it flawlessly.
  • It’s easy to use and understand.
  • It’s efficient and robust.
  • It’s easy to maintain, upgrade and scale.
  • It’s secure and reliable.

How can we build high-quality software?

Let me present to you a life-changing rule:

Be -> Do -> Have

If we want to have high-quality applications serving the users, one of the first questions that generally comes to mind is “What should we do?”. But what if we ask ourselves:

Who should we BE (or “Who should we become?”) in order to be able to DO what it takes to HAVE high-quality applications serving our users? 

Quick Tip: This rule applies to almost all aspects of your company and even your life. 

Following this rule, here are some tips you can apply to different areas of your company.

Focus on your company/team first

Software is built by people and intended to solve their needs. So, people should be the first thing in our minds. The team is the driving force behind every aspect of the software development process, from planning and design to testing and maintenance. They are responsible for defining the features, building the software, and fixing it when it breaks. So, basically,

Your software is going to be only as good as your team/company is.

A product in any industry is a reflection of the team that builds it; the team is a reflection of the company they work for and vice-versa. You can see evidence in your daily life that high-quality products are generally built by great companies, and bad products by bad companies. Think about the personality and skills of each team member and how the way they think and do their job is reflected on the final product.

This is a very extensive topic, and there’s no magic formula for building a great company. However, you can always look inside your company and think about how to improve it, along with your team and yourself.

Quick Tip: If you are a Technical Manager, you may be interested in reading the article “What’s a blended seniority team and why you need it in your software project,” written by our CBO Eugenio Diaz Lis. This article discusses why highly-qualified teams aren’t necessarily formed exclusively by senior software engineers.

Build a culture that matches what you want to achieve and how you want to achieve it

The culture of a company is one of its most important pillars. It’s part of the company’s DNA and has a strong power to drive results effectively. Talking about culture is talking about people. So, here are some tips to consider when thinking about your company’s culture:

  • Commit to doing things right. Don’t take shortcuts, and strive to plant that seed of thinking on your teammates. 
  • Build a culture that empowers innovation.
  • Inspire confidence and allow your team and yourself to take risks and make mistakes. This is a natural part of innovation, creativity, and experimentation.
  • Build synergy. Happy employees tend to perform better, are more proactive, and take more initiative than those who are not.
  • Foster positive relationships and a sense of belonging among team members. Many people develop stronger connections to their teammates than to the company, product, or role. Therefore, team building is crucial for motivating employees to work, help, contribute, and innovate.
  • Ask yourself every day, “How can we do this better?”. This applies not only to the technical aspects, but also to the organizational and human aspects.

Quick-Tip: Take a look at this article from Mercedes Jauregui, our HR Manager, to know more details about how we achieve this at Patagonian.

Take the necessary time and effort to plan

Planning refers not only to the phase prior to the execution of the project. It also involves making decisions, having discussions, deciding which technologies are going to be used, which libraries, which tools, and even doing some Proof-of-Concepts along the way. 

Nowadays, with agile methodologies, planning is a constant in the software development process. Sometimes, however, the value of planning is underestimated and teams or stakeholders feel that they are not being productive when taking time to plan rather than developing new features. 

Taking enough time to make decisions today will save a lot of time and money in the future.

Quick Tip: It is always better to have an app with a couple of very well-done and robust features that solve the users’ needs than an app with a lot of buggy features that people just don’t want to use. 

Write high-quality code

We have high-quality code when the codebase is:

  • Consistent / Standardized
  • Legible / Understandable
  • Easily Debuggable / Testable
  • Maintainable
  • Reusable
  • Scalable

There are a lot of factors that can influence the quality of code, both for better or for worse. Let me share with you some strategies that I’ve found helpful for addressing the first three items:

  • Write clean code. Always have in mind that we write code for other people and for our future selves. There are many resources available, including documentation, courses, videos, and examples, that can help you learn how to write clean code properly.
    Quick Tip: Also, don’t be afraid to reject a Pull Request if a variable’s name is not well written.
  • Set up linters and code validators that could work on your local machine. Examples: ESLint, TSLint, Prettier, Detekt, Pylint, SonarQube Scanner (local lib), etc.
  • Prevent commits of code that don’t follow the linter and validator rules. There are tools that allow you to execute those validations at the moment of submitting a commit or pushing some code and prevent them from being done if the validations fail.
  • Configure main Git branches (like ‘main’, ‘master’ or ‘develop’) to be protected. This way, for example, you can ensure that only code that has been reviewed and approved via Pull Request gets merged into those branches.
  • Review Pull Requests in detail. Take the time to review PRs. Do it when you are not in a rush. Focus not only on the code you’re reading, but also on the already existing one, think about its consistency, if it follows your team’s agreements and best practices, and any other criteria you have defined with your team.
    • Quick Tip: Another very effective strategy that I’ve found, especially for complex features, is to review the PR in a meeting with the author. This lets you understand the context and the feature a lot easier, have discussions about the code live and solve doubts faster.
  • Be sure that each Pull Request has (at least) a well-written Title, Evidence (screenshots, videos, etc.), a Description, and a Ticket related to it. This will ensure mainly two things: 1. The people reviewing the PR will understand it. 2. You’ll have a clean commit history on the repository, so it’ll be easier to track changes and solve issues by knowing when some specific feature was included.
  • Make each PR pass through a pipeline that builds the code, executes its tests and makes other validations. Jenkins is a very popular tool to implement this. Also, SonarQube is widely used to check Code Quality, Code Smells, Test Coverage and many other validations within the pipeline.
  • Follow the best practices of your project’s language and framework. Dedicate time to do some research, read articles, documentation, forums, watch YouTube videos or learn through courses these best practices.
  • Write Unit and Integration tests for your code. Having good test coverage on your code eases debugging exponentially! We’ll talk about Testing later in this article.
  • Write Documentation. This is one of the most important things to do, especially for big and complex applications. It saves time, money, effort, and stress and eases the maintenance and upgrading of the application. We’ll talk about Documentation later in this article too.

Maintainability

Basically, if all the above tips are followed, you will end up having a project that can be easily maintained now and in the future. So, we can summarize it as the combination of:

  • Clean Code
  • Best practices of the specific language and framework
  • Unit / Integration tests
  • Documentation
  • Defining a branching model for your repository according to your needs and following it strictly

Reusability

Reusability is sometimes hard to achieve, and will depend a lot on your project’s structure and architecture and your team’s knowledge. Every existing programming language and every framework has their own particularities. There are good practices to follow and bad practices to avoid. But there’s one principle that is applicable to everything and is the base for reusability, no matter what you are coding: The Single Responsibility Principle (SRP)

This is the first one of the SOLID principles that Robert C. Martin defines like this: “A class should have one and only one reason to change, meaning that a class should have only one job.” 

Even though the SOLID principles are related to Object-Oriented Programming, the SRP concept applies to any programming paradigm. What the SRP states is that every class, module, or function (or whatever your program uses) should have only one responsibility. It should do only one thing.

If you write code taking this into account, it will be a lot easier to achieve reusability. It also becomes a very powerful tool when you combine it with two concepts from Functional Programming, called Pure Functions and Immutability

Pure Functions are pieces of code that always return the same result if the same arguments are passed, i.e., their behavior only depends on their arguments, and there’s no other thing that affects the result. Applying this, together with the Immutability principle, will produce functions (or classes or modules) that only do one thing and work with the data you pass them, without modifying it, making it a tasty recipe for Reusability.

Scalability

It means that a piece of software is easy to upgrade and to add new features to. It also means that, thinking about infrastructure, it’s easy to serve bigger amounts of users when needed. At least for the first meaning, following all the tips discussed in this section will lead a software solution to be easily scalable, thinking about the code.

Automate as much as possible

Automating reduces human error and gives people and the company the most valuable asset they have back: Time. Automating will give people time to think, to develop, to plan, to innovate and to add value to the product.

There are many things that can be automated, but here are the most common ones:

  • Tests and validations when a Pull Request is submitted
  • Software builds
  • Deployments to certain environments when a Pull Request is merged
  • Deployments to production
  • Monitoring and alerting about issues
  • End-to-end testing of applications

Give testing the relevance it deserves

I’ve often observed that developers and managers are pleased with a new feature as soon as it’s developed. However, when it comes to testing, they view it as a time-consuming process that delays the release of the feature, and they begin to prioritize new feature development instead.

I know that the need to move fast is always there, but it’s better to move at a solid pace and deliver very well-made features. Speeding through the development process will slowly build a snowball effect that will cause you a lot of headaches along with causing the waste of lots of time and money in the future. Plus, you’ll lose a lot of upset users that probably won’t use your app anymore.

I’ve also noticed a trend in many companies where, when things get tough, the QA team is often the first to go. As someone who has worked in the industry for several years now, I cannot emphasize enough how much of a negative impact this can have on the development process. We need to recognize their importance in ensuring that the final product is stable and of high quality. 

Now, let’s talk about Unit Tests, Integration Tests, Automated End-to-End Tests. They are a very effective way to guarantee the integrity of the project and ease the refactor and upgrading of existing features. As a developer, there’s no more satisfying feeling than working on a codebase that has very good test coverage, knowing that you can peacefully develop your feature and, if you break something, it will appear on the tests.

Quick Tip 1: Check out Test-driven Development (TDD), which is one of the most popular and effective programming practices to write modular, robust and easily testable code.

Quick Tip 2: If you are a developer, try to break your own feature while developing. Think about all the possible scenarios that may break it and develop thinking about that.

Quick Tip 3: Don’t forget to do testing with real users and include ways to get user feedback or for them to report issues on your app.

Give documentation the relevance it deserves

Along with the lack of testing, the lack of documentation is one of the most common pain points in a software project. Sometimes, the departure of key personnel from the company can result in the disappearance of critical knowledge that they alone possessed.

This issue has several justifications (or, rather, excuses) behind it. The most frequently cited ones include lack of time to write documentation, the team’s concentration on new feature development, prioritization of other tasks, or even a general dislike of documentation writing among team members.

Documentation is so incredibly powerful that a document that takes you 1 day to write can save another person 1 week of investigation, or sometimes a whole sprint for a team. By adopting this approach, the project can enjoy simplified onboarding of new members, minimal impact of staff rotation, hassle-free maintenance, and considerable savings in terms of time, money, and stress.

On top of that, don’t forget that:

More important than writing documentation is maintaining documentation.

If you find it hard to make time to write or update documentation, give it the relevance it deserves. Create a ticket on your sprint for it, assign it story points and define a deliverable for it if you need to. The importance of documentation is so high, that we shouldn’t try to find spaces in our free time to write it (as it happens on many teams). We should dedicate time and resources to it. 

Spoiler alert: Your code will never be perfect

If you are a developer, let me tell you that no matter how proud you are of the code you write today, you will see it a year later and you will say to yourself “What was I thinking when I wrote this?” That’s ok, it’s part of the process of improving your skills, and this industry moves so fast that, in just one year, there will be better solutions for the same problems. Don’t be obsessed with perfection; just try to do things the best way possible.

If you are a manager, it’s important to know that technical debt is an inherent part of software development. Complete eradication of technical debt is unlikely, and in fact, a lack of technical debt may be an indication that your project is not evolving. Evolution comes with experimentation, making mistakes, evaluating different solutions, trying them, releasing them, and iterating over them. So, my advice is to implement strategies to try to reduce technical debt and to manage it through time. But don’t get obsessed with it because it will always be there.

Final words

Programming is an art, and programmers and every person involved in the process can be considered artists. A piece of software is a piece of art that solves a problem. The way the code is written and the way the software is built will be different from person to person, from team to team and from company to company. That’s the beauty of software: Even though it is meant to be understandable and executed by machines, it is not an exact science and every new editor tab is a blank canvas ready to be filled with everyone’s art expressed in lines of code.

As I said at the beginning, there’s no magic formula to build quality software. However, I hope this article and the tips that I shared here inspire you to build high-quality software and to evaluate your company, your team and yourself to see in which areas you are excelling and which ones deserve more attention. 

Happy coding!

By Edgar Bonilla

Senior Software Engineer at Patagonian. I consider myself a T-shaped engineer, having expertise in web and mobile software development, along with a wide range of complementary knowledge in non-technical areas. In my free time, I enjoy playing drums, doing some exercise, and spending quality time with my wife, family and friends.

Leave a Reply

Your email address will not be published. Required fields are marked *