Painless Software Quality — Code Defensively, Fail Early & Test Continuously
The “Painless Software Quality — Code Defensively, Fail Early & Test Continuously” is taken from The Jumpstart Up a technical book that I am writing to provide a practical handbook and experience-based guide focused on the application to lead DevOps and Agile transformation.
In 2002, the National Institute of Standards and Technology (US) published a report in which software bugs costs were estimated to $59.5 billion every year in US. The study estimated that more than a third of that amount, $22.2 billion, could be eliminated by better testing.
Software is not just about coding, it is about the whole eco system.
Abstraction & Automation
First thing, let’s start with the definition.
Software are mainly about:
- Abstraction: Abstraction is the process of translating the system complexity to a human friendly user interface -> Your website, your web platform or your web app.
- Automation: Programming aims to automate performing a specific task or solving a given problem.
Abstraction needs managing complexity and there are many ways to do this, but most of them are bad ways.
Automation needs awareness because automating a bug (for instance) results in a bunch of bugs in your live production systems.
Software are made to resolve a problem, they are not intended to be the problem.
In my experience I have used good software and I have seen bad software, there is no middle.
As a user/customer, if an online application returned “500 internal server error” response message more than 2 times, I will use another one. I will choose the software with less features and a good quality because a buggy software even with more features is a headache.
Anyway, as a business owner you are not obliged to choose between features and stability and as a software engineer you should find the strategy to evolve the whole ecosystem without regressions.
The following practices are a point of entry to better manage your software quality.
Code Defensively, Fail Early & Test Continuously
Defensive programming is a way to design software in order to avoid failure under unexpected circumstances. It is intended to build software with better quality and user experience.
A secure software should be “secure by design”.
Protecting yourself against flaws and considering security risks during programming is the only way to do defensive programming.
Security is not a layer to add after a development sprint, if you are doing you have a problem.
From the ground up, security should be constantly encountered to grant that vulnerabilities are discovered when a user inject sql, listen to your network traffic or steal your data.
A common checklist is to:
- Authenticate your users
- Encrypt your transmitted data
- Use proven and commonly used methods for both authentication and encryption
- Protect against farming, cross-site request forgery, sql injection and other known risks by: validating inputs, securing parsing ..etc
A piece of code is secure by default if the developer consider that all the data is important, all the code is unsecure, all transmissions are flawed until proven otherwise.
I have read many blog posts explaining that defensive programming is just rubbish.
In defensive programming, the application should have a behaviour to handle unexpected conditions in order to secure the environment and give the customer a good user experience. Is this bad ?
Do you think that XSS and SQL injection attacks and buffer overflow are funny ?
Do you think that throwing an error stack trace, ignoring security flaws and illegal conditions is better than handling the error and sharing with your users understandable error messages and explanations ?
“Secure by design” is not just securing the core of your software, but it is a continuous process, security should go in parallel with your code.
Security should be a key consideration in design not an overlay.
Defensive programming can be done in multiple ways, just like any programming task you can do, but there are few ways to do it right, check the way you design your software and set your limits: don’t do “over defensive-programming”
“Fail-fast” in software development is letting operations stop and fail as soon as there is an unexpected error. The opposite of this is failing silently like it is the case of catching all the possible exceptions:
except Error_A :
except Error_B :
While catching exceptions is a good way to avoid failures in a production environment, letting operations fail as fast as they can is in many cases a good alternative, but remember that “fail-fast” is a delicate approach used to harden the robustness of your code only during the design/development.
Failing fast will give you an early visibility about errors and will stop an operation rather than attempt to continue a potentially defective process (like deleting data).
During development and only during development, you should consider using the “fail-fast” approach.
Continuous testing is one of the pillars of DevOps and modern QA (Quality Assurance). Create your continuous integration and delivery toolchain and implement testing within the global process.
Continuous testing is a great way to learn while being agile and take risks. Create a workflow that encourage continuous and instantaneous feedback about the quality of the API:
- The repetition leads to mastery
- Improvements are done safely through experimentations and iterations
- Experimentations are safe
Testing feedback should be done in a short time so that you are not forced to choose between evolution and stability.
You may think that hiring experienced developers or hiring more developers could help you stop having bugs, this is wrong.
Some development tasks could be complicated, a developer will find himself buried under the complexity of component dependencies and interactions of a system elements and in the same time should add a new feature to the whole block.
Testing continuously will insure that your production environments are more stable. Bugs discovered during the development phase are less expensive to fix than production bugs.
While developing a feature, the whole focus could go for the desired result not the surrounding environment, continuous testing will focus on both.
Continuous testing is a pillar in the DevOps culture so embrace the whole DevOps.
DevOps is culture that requires some practices and a new vision, its common goal is unifying people and organizations around unique goals.
DevOps is not only a business need but a technical sets of tools and practices in order to create better software quality. Check my post about DevOps: The 15-point DevOps Check List
Kiss, Dry & Yagni
These are general recommendations that every developer should know and respecting these standards will result in a maintainable, flexible, readable and a testable code .
KISS “Keep It Simple, Stupid!”
The simpler your code is the simpler to maintain it and to work on its evolution. In software engineering, a complex system will work for you unless its components were developed on accordance with this rule.
Don’t just use your programming language fancy features just because you can.
DRY “Don’t Repeat Yourself”
Like I said above, programming is about abstraction and automation.
If you, as a coder, start to repeat a line or a block of code, create a new abstraction to reuse it.
YAGNI “You Aren’t Gonna Need It”
YAGNI is a principle of extreme programming (XP) and it advocates for avoiding extra code or extra features that you may add just because you think that “you will may be need them in the future”. An experienced developer knows that in the overwhelming majority of cases, you will not need this.
“Do the Simplest Thing That Could Possibly Work”.
Don’t Move Forward Before Fixing Your Bugs
Developing new features is certainly funny to do but software development is not done right if you do not fix bugs before writing new code.
If you find a bug in some code that you wrote a few days ago, it will take you a while to hunt it down, but when you reread the code you wrote, you’ll remember everything and you’ll be able to fix the bug in a reasonable amount of time. ~ The Joel Test: 12 Steps to Better Code
In software engineering there are 4 types of maintenance:
- Corrective maintenance in order to correct discovered problems after delivery.
- Adaptive maintenance in order to keep a software stable after an change in the environment.
- Perfective maintenance in order to ameliorate performance, maintainability or operability.
- Preventive maintenance in order to correct potential faults before they become effective faults.
These 4 types are important to the success of every software development project. If you lack one, your business will for sure encounter some impacts.
You Have One Chance
Sometimes, you will not have more than one precious chance. It is the case at least for a specific software: Public APIs.
There is an interesting fact about APIs:
Public APIs are forever: Only one chance to get it right.
Honestly, I don’t remember where I read this the first time but it is a known assertion.
Creating your own public API is a technical choice driven by a business need: sharing your structured public data more efficiently and interacting differently with the external ecosystem.
Once the API is public and your customers start building applications or collecting data using that API, it becomes harder to change it because you will break what other developers or your customers have done.
If changing your API is an urgent requirement think about creating a new version while keeping the first one. Encourage customers to move to the new maintained API but keep supporting the first one unless you don’t care about your customers or political problems.
Even if you create a second version of your API, remember that your first one needs maintenance and support so you will have spend money and time while you will do the same thing on the newest version. So be sure while designing and building your API, DevOps can help you a lot with this.
Design To Secure, Code To Fail & Test To Learn
Defensive programming, failing fast and continuous testing are the three main points here.
You should consider using “fail-fast” while programming defensively. These two approaches are not mutually exclusives, failing-safe is even a way to secure your API in many cases. Many developers may argue that it is a defensive programming practice.
Writing a good software with a high quality is not hard at all, but it could be so painless if a team think that quality, security, operability or fault-tolerance are just layers to add after the development is done.
This reminds me the waterfall methodology and honestly if you want your business to be agile and stable at the same time do not adopt a 100% waterfall model.
Start working in short sprints (2 weeks) and during a development sprint the code should be coupled to continuous testing to pick out bugs as soon as they are visible, notify developers and fix your code.
You Will Fail Anyway So Fail Gracefully
It is normal.
In the theory of software evolution, an initial software when repeatedly updating it for various reasons will generate bugs.
When I evoked the “fail-fast” approach I was referring to the process of development not the production. “Fail-fast” will allow you to discover the maximum of bugs, but any discovered bug or unexpected condition should be handled before moving to production: If something will fail in production, it should be catched by program and gracefully handled.
Software may fails and handling this is a technical responsibility:
- While designing your software think about its operability: IT teams should able to monitor, stop, start, restart, redeploy and do other operations easily and fast.
- Build a fault-tolerant code or make sure that in the case of a failure you have a functioning alternative solution.
- If your code crashes, it should be able to start back up.
- Work in collaboration with Ops teams not just to deploy your code but let them engage during the designing phase.
Hello ! This reminds me something :-)
The “Painless Software Quality — Code Defensively, Fail Early & Test Continuously” post is taken from The Jumpstart Up that I am actually writing to provide a practical handbook and experience-based guide focused on the application of DevOps and Agile transformation.
What I love about software engineering is not just the code but the global process and the general vision that one can have from architecting, coding, testing, deploying to maintaining a live stable software and all of the iterative short-term feedbacks that you can implement in the global process of your software development: An architecture for your digital transformation.
If you resonated with this article, please subscribe to DevOpsLinks : An Online Community Of Diverse & Passionate DevOps, SysAdmins & Developers From All Over The World.
If you liked this post, please recommend and share it to your followers.