Robustness is one of the most important qualities of any application, is also one of the most difficult to achieve. What follows is a list of what in my opinion are the 10 principal characteristics to be considered when designing any robust application.
Having the right transactionality in your application is critical to guarantee the integrity of the data. No matter which framework you use to manage the transactions, is important that you consider making transactional the system calls that require ACID properties. Some key areas to look for:
- Transaction modes.
- Transaction frameworks and APIs.
2.- Database access.
Modern frameworks usually try to abstract the access to the underlying database of the application, while this is certainly good, is also causing an increasing lack of understanding of what is happening behind the scenes. Being able to explain what is the SQL likely to be executed by your framework, and when is going to be executed will help you to create better database access code, and to optimize the database access, specially if you don’t have a DBA in your project. Some key areas to look for:
- Lazy loading.
- ORM frameworks and APIs
Multithreading is the next big thing in software development, is been there now for ages, but with the new hardware architectures going multi-core, multithreading is emerging as the new must-know skill. It is also critical to acknowledge that your code is probably already running in a multithreading platform so you need to consider it to prevent your code to fail. Some key areas to look for:
- Stateless services
- Unmodifiable beans
- Race conditions
4.- Logging and auditing.
Logging and auditing are one of the most underrated qualities of an application, especially for server applications. Is easy to forget in development how useful a log is when the application has already been rolled into production. Some key areas to look for:
- Consistent identifiers. Try to use the same identifier in every log message so is easy to track one thread of execution.
- Consistent format: If you have to grep or quey your logs, you will appreciate using always the same format for your log messages.
- Completion: It should be possible to track an entire thread of execution from the log being able to see the main events.
5.- Exception management.
An exception that gets thrown out to the user level is usually a sign of a fragile application, a proper exception handling can make you application more robust. I already wrote an article with some basic considerations regarding exceptions.
Invariants are conditions that need to be kept across several data structures to guarantee the integrity of the logic of the application. There are several design techniques or language features, as encapsulation, generics, enums… to help keeping the invariants safe. Something so simple as changing a list of constants to an enum or encapsulating two attributes that need to change in synchronization can make a huge difference building a more robust application.
7.- Disabling/Enabling/Swapping services or modules.
Shit happens, so you better be ready, being able to disable a service, or a module, or being able to write a dummy implementation to overcome a temporary incident, accounting for this in development, may save your day.
8.- Ubiquity and low coupling.
Being able to run chunks of your application independently, and at any time, is a sign of good design, and can also help you recover for unforeseen errors.
9.- Self diagnosis.
One very valuable feature of any service call is to provide with self diagnosis on error, so the cause for the issue becomes obvious and it facilitates the search for a solution.
Any non trivial system requires documentation. Good documentation made available to the right people can save you from many errors.