Holistic Quality Assurance Practices for Software Development
One of the golden rules of software development is not to deploy anything to production on Fridays – primarily because nobody wants to risk their weekend working on a production incident after erroneous code gets accidentally released.
But what if comprehensive quality assurance (QA) practices were incorporated into the software development processes to make it safe to deploy on Fridays? Although I would probably still leave the deployment to Monday, a holistic approach to QA is hugely beneficial in the long run. Let's explore each of the elements in more detail.
Development phase – Building the spaceship 🛠️
You can build a frighteningly tall building on top of a strong foundation. Similarly, investing in QA early on in the development process pays off later when your codebase grows – ultimately even speeding up releases.
It might not seem intuitive at first, but writing readable and comprehensible software makes up a great deal of QA. Understanding the reasoning behind the existing software design enables developers to make well-educated decisions on how to improve it and what things to take into account when making changes.
A few things you might like to consider in development:
Linting and formatting
Running pre-configured linters on your code to check for potential mistakes is pretty much a no-brainer to save you time for more important tasks. Equally, consistent formatting and structure in your codebase make it easier to follow even longer chains of logic and, thereby, spot errors and blind spots in your train of thought early on. With dynamically typed languages like Python and Javascript, type annotation tools further support readability, checking for obvious bugs and indirectly improving documentation.
Code review
People think differently, and you should definitely make the most out of that. Although you might deliver flawless world-class code, your colleagues might still spot something in your implementation that you’d otherwise been blind to or suggest a completely different approach. As a bonus, you also learn a lot from reviewing other people’s code.
Communication and specification
It’s easier to make robust software when you fully understand the business case. When people work on something intensively for a long time and finally hand over their ideas, they tend to forget to fill in some business-critical details that they take for granted. Ask stupid questions and challenge your colleagues' ideas.
Integration and release phases – We’re taking off 🚀
Releasing a software feature that you’ve worked on tirelessly is exciting. Getting a good night's sleep after it’s been deployed is even better. Software testing practices give you this peace of mind – they are widely adopted and an industry-standard in software development. However, there’s always an overhead cost to testing, and therefore you should carefully consider what elements are critical to test and what kinds of scenarios the tests attempt to simulate.
Automated testing
Write once and enjoy a thousand times over. When improving your software’s testability, consider the use case and granularity of each functionality.
Unit tests make sure that the building blocks of your software are deterministic and handle even unexpected payloads correctly.
Integration tests validate that what goes into your software comes out nice and tidy on the other end — that is, longer chains of components and logic produce the expected outcome.
End-to-end (E2E) tests simulate actual user behaviour and are especially useful for verifying that the most critical user paths are functional on various client devices. Each type of test has its purpose, and they all complement each other.
Manual testing
Test your software as if a real user was using it and incorporate other people – for example, from business roles – to validate your implementation. Remember to be innovative in exploratory testing and deliberately try to break your software to identify any blind spots.
Production phase – We’re live 🌌
QA doesn’t end with deploying the application to production. Instead, QA gets even trickier and, possibly, more critical after the software is live. Providing the means for gathering customer feedback is beneficial for identifying bugs and errors that have gone unnoticed so far. However, users aren’t necessarily driven to make your software better – since that’s your responsibility, after all. Therefore, it’s all the more important to build processes for identifying production issues in real-time and to proactively address them.
“Trying to anticipate every possible production issue scenario only takes you so far, as unexpected incidents may always occur regardless. Therefore, the ability to react to unexpected incidents as quickly as possible is more important.”
Here are a few practices to get you started:
Recovery and service robustness
When errors do appear in the application, being able to recover from them is essential. Identifying potential sources for errors in the application – such as 3rd party APIs, unusual user input, server error responses, etc. – and designing error boundaries that limit exposure and enable the system to recover from those errors ensures that business-critical elements in the software remain functional. For example, an e-commerce site might want to provide a variety of payment method alternatives to guarantee that a paying customer can carry out their purchases regardless of whether one payment method suffers from temporary downtime.
Reporting
Reporting errors and their technical details (e.g. stack traces) helps identify and reproduce consistent bugs in the application. There are many reputable service providers for this purpose, such as Sentry.io.
Monitoring
Even if no errors are reported in production, the service might be suffering from a latent incident affecting business performance. Take the e-commerce site as an example. There might be a faulty redirect link specified in the payment process, preventing users from finishing their orders. Building a monitoring setup and shaping a set of well-defined KPIs for the desired service behaviour allow you to identify anomalies and, thereby, react to abnormal behaviour.
On the e-commerce site, if we track the number of purchases per hour, we can tell that something is going on when purchases suddenly drop to zero or deviate drastically from hourly averages. Monitoring production doesn't prevent issues from happening, but it enables you to take action promptly to mitigate the damages. Expect the unexpected – build means to track and monitor something you couldn’t predict beforehand.
User behaviour analytics
Even if your software was built technically flawlessly, the user experience might be unintuitive nevertheless. Having zero bugs on an e-commerce site doesn’t help much if users don’t find where to open their shopping cart. Track and analyse user behaviour with user analytics.
Iterate 🔁
When issues appear in production, there’s a concrete case for extending your QA practices to cover similar problems: writing a new automated test to cover an identified corner case or adding monitoring to keep an eye on issues in a 3rd party service. Learn from your mistakes and prevent them from happening in the future.
Safe coding!