Blog

Benjamin Franklin once famously said “in this world nothing can be said to be certain, except death and taxes”. For the software developer, the saying should be amended to read “except death and taxes — and software bugs.”

It’s an unfortunate fact that the very nature of software development, especially in the collaborative environments popular amongst coders and companies today, makes software bugs inevitable. As APIs and the systems that drive them have become more and more complex, so too has the underlying code that powers it all.

How should developers deal with these bugs then? Today, we’re going to discuss some tips and tools for not only detecting bugs, but dealing with them once discovered — and how to potentially keep bugs from ever becoming a headache.

Internal Support Failures

One of the most common causes of API failures is a failure of internal coding action and decision. Developers typically tend to implement dependency-specific or protocol-specific solutions for common issues — after all, why reinvent the wheel? And more often not, these solutions are just fine — but sometimes, errors can occur when the various dependencies that a system relies on are incorrectly cited or implemented locally. Here are some ways to avoid that:

Check to see HTTP/2 is properly implemented
Failures can be very basic. For instance, the failure to properly implement HTTP/2 can have disastrous results, causing failure in client-server negotiation and causing timeouts through increased latency. Even worse, HTTP/2 failures can cause breaking of compatibility with HTTP 1.1 methods and header fields, breaking functionality as simple as GET and POST.

Ensure you handle SSL Certification & SSL renegotiation correctly
Failures can also be much more complex. For instance, SSL Certification revocation and reissuance can be a problem if handled incorrectly. When a server’s domain changes, for instance when a person transfers data from a geographic domain to another, mismatched SSL handshake data will fail. The same can be seen during SSL renegotiation, where an anonymous user makes themselves identifiable. If either of these cases are improperly handled, SSL failure will cause immediate and complete loss of communication.

Adopt standards like UTF–8
Internal support failures may even not be “internal” in the strictest sense of the word. Expecting content to be strictly in ASCII or ISO characters can cause a complete meltdown in communication when presented with foreign or Unicode characters. Even something as simple as time discrepancies between timezones can break calls. Adopting standards like UTF–8 can go a long way to fix this, as can sanitizing your inputs.

Code for the worst case scenario
Sometimes, the issue lies in simple mistakes rather than large ones. Even though these mistakes might be small, their impact might be huge. Failing to close a statement or class is quite easy to overlook and can fundamentally break an API.

How are these failures handled? The succinct answer is “diligence and awareness”. Do not code APIs and expect them not to be tampered with — code them for simple, basic functional usage in the worst case scenario. Code as if your client is going to request using foreign characters, on a leap-second time zone shift, with an expired certification. Assume that every API call made is going to fail, injecting failure into the system itself — and then develop accordingly to fix those problems before you face them in real life.

Develop routing and fallback loops
Beyond this, simply being aware of the possibility of system failures is important. Develop routing so that the issue is isolated to a single server, and have a failback loop to ensure data is still handled by servers using confirmed, accurate, and effective system setups.

Virtualize Your API

One of the best ways to ensure an API is as good as it can be is to take it into the theoretical space. APIs are living, breathing entities, and often, even if there are failures, they might be ignored or even invisible because of complementary systems, failback loops, and other such systems. The solution is to virtualize your API, and test it in a virtual space. A virtual API is a sandboxed API — isolated from all the potential supports and failback systems to ensure that the API at its most basic level functions as intended.

Test first on a crutch-less, virtualized API
Performing your API testing in this way has some very important advantages over real-world testing. First and foremost, you are testing an image of an API — not the API itself. This removes any potential interruptions in service between your API and its consumers, and removes the aforementioned crutches the API might depend on. The other big benefit to this type of development approach is that API response is predictable and known. When developing in isolation, the expected response is unchanged by the gauntlet of differences in servers, processors, clients, and various hardware items, and thus, errors in response, even minute ones, can be targeted specifically.

Throttle worst scenarios to the virtual API
How then do we test in the virtual API? Make a list of the top ten “worst case scenarios”. Incorporate memory overflow, poor code forming, expired certifications, and more. Then, test permutations therein. Make every possibility the starting point of five more possibilities. Test common errors — if an API calls content from a server, how does it react to a thrown 503 error? What happens when data triggers an unauthorized client error?

Don’t consider only “accidental” errors either
Assume someone is trying to break into your underlying systems, and has found a weakness in the API architecture — then attack it relentlessly with as many variations as you can create. What we’re doing here is essentially “trial by fire” — it’s better for the API to undergo all this damage, testing, and manipulation while still in the virtual form than it is to identify these problems on a live production environment.

Test the hardware the API is set up on
Accordingly, testing the hardware the API is set up on is vitally important as well. Because we’re in the virtual space, deploying the API on a variety of hardware is as simple as closing the virtual machine, changing parameters, and then opening it again. How does the API handle with different common processing and memory configurations? What happens during a cluster failure or HDD read error? What does an API do when threads are reduced, or response delays are increased?

A virtual system can prevent cataclysm
All of these variations can have huge effects on the API and the data it handles, and simulating the changes before they’re implemented is incredibly powerful. Consider the following: as your company deals with heavier and heavier traffic, it’s been decided that the main server systems need to be migrated to a new server cluster. As the API is transferred, requests suddenly grind to a halt, and the system stops working. Upon inspection, it’s revealed that the new server cluster had limitations in the reserved thread count, and the amount of data being processed was overburdening some servers with poor load balancing feature sets. The API was not designed to handle such restrictions. Because of this, the API is down for over four hours, with a SLE (single-loss expectancy) in the thousands… This failure could have been isolated, documented, and prevented in a virtual system — before it cost the company thousands of dollars. And it was as simple a procedure as creating a virtual image and spending a few days testing.

Third-Party Tools

There are of course a wide range of solutions for bug tracking addressing these issues. While it would be almost impossible to list every single API testing solution and platform available, there are a few notable candidates that would be useful for specific situations. These are not endorsements — these solutions are simply representative of their field and of effective solution implementations.

Implement open source API monitoring
These tools vary wildly in their application and methodology. Some solutions, like API Fortress, have opened the doors for testing and monitoring to even those without heavy code experience using graphical interfaces. This sort of solution is great in that it’s not only testing the functionality of a given API, but the speed and accuracy of its data delivery as well. Additionally, because all the testing is constructed and run in-house by in-house developers, there’s a much more intimate relationship between the developer and the code being developed.

Or, consider third party managed solutions
On the other hand, third-party solutions managed by third-party entities are equally effective, and may be a great choice when API codebases are large. Solutions like Runscope allow for active monitoring and alerts integrated into solutions like PagerDuty, Slack, and even base standard email. While this is as valid a solution as any, there is something to be said for the dangers of offloading your monitoring and testing to an external provider. By removing oneself from the identification and repair process, more errors and bugs are prone to enter the codebase — then again, removing much of the weight from your team may allow for quicker development, at the cost of the third-party integration.

Find errors with optimized testing processes
Some solutions are actually more like “portals” or “tools” to implement your own testing processes. POSTMAN is an HTTP client for APIs that allows simple, clear queries to be constructed and saved as presets. This removes much of the abstraction inherent in API consumption, and makes clear errors that might otherwise be obfuscated by code complexities or rendering issues. POSTMAN also allows for the collection of endpoints for large-base testing, which is significant, especially when narrowing errors down to specific parts of functioning API codebases. This is important when testing authentication methods as well, since multiple endpoints can be tested and revised against to ensure secure endpoint responses.

Use cURL commands without the complex command lineHTTPie is a similarly powerful solution, allowing developers to use cURL commands without the famous complexity of the command line utility. For instance, a simple query such as this:

This removes a lot of the complexity of running cURL commands, and when extrapolated to larger, long-form commands, the HTTPie alternatives are much clearer as to what they’re intended to do, and how they do it.

Conclusion: Test Often, and Test Completely

We’ve previously discussed the concept of developing with a long-term mindset before, and its importance is reflected in the approaches detailed here. Develop for the needs of your consumers now, while keeping in mind the needs of the future user — part of fulfilling this is ensuring that you don’t introduce or ignore current bugs for the sake of functionality.

Test often, test completely, and test with the worst-case scenario in mind — this should be the hallmark of any developer worth their salt.

About Kristopher Sandoval

Kristopher Sandoval is a web developer, author, and artist based out of Northern California with over six years of experience in Information Technology and Network Administration. He writes for Nordic APIs and blogs on his site, A New Sincerity.