Over time we all develop best practices for whatever it is we do every day. For the last ten years or so we have tried to summarize the guidelines for writing applications, dealing with relational databases and other practitioner "learnings". For the benefit of our clients we would like to share some of our internal materials that are used as "rules of thumb".
Note that these rules apply to commercial work only - some of the guidelines, like licensing, are not valid for our open-source work.
Another disclaimer is in order - what follows is largely based on this author's personal belief that good software is best built in what I call "Agile-Benevolent Dictatorial Democracy". Or, using small words:
- Agile processes are good as long as
documentation is not forgone;
- Ability for everyone to contribute ideas and opinions at any time is good;
- Dilution of ultimate responsibility for shipping a working product is bad;
- Responsibility without authority is bad;
Taking all of these into account means that good software is built by teams in which there is a clear and ultimate decision maker, who is able to triage daily tactical decisions and priorities as a balance between the must-ship date and quality of engineering.
So, without any further ado - here are the Rules of Thumb for Building "Fat" Applications. Many of them also apply to building browser based apps as well - in fact the difference is disappearing so fast, that we are likely to have a single unified guideline pretty soon.
Fat App Programming Rules of Thumb
(aka How I wish I could write UI Applications)
Version: June 6, 2007
Purpose
These guidelines are reasonably general, but specifically written for developing UI-centric applications. In other words the “client” side of client-server, or the “presentation layer” application.
Usage
These rules are axiomatic. They are expected to be followed in their exhaustive entirety. Cutting corners is of course possible but in a commercial environment should require specific in-writing approval on a case by case basis.
This document is written with enough detail so that the author can know exactly what it means – this does not necessarily mean that there is enough detail for other people to understand it. If it does not make sense – please ask - do not assume. Assumptions make bad excuses.
Rules in no particular order.
- Easter Eggs.
- Easter eggs are absolutely prohibited.
- Copyright in every source file.
- If you have not been given a text header, containing the proper copyright information to include in every source file, please ask immediately.
- Version and Build number displayed prominently.
- The application will show prominently the version and build number as a part of the main UI.
- Free and 3d party licensed tools
- Unless specifically authorized do not bother evaluating or assume that you can use any software or software component, which is licensed using a royalty scheme
- It is OK to evaluate libraries and components for which we can pay once and then use in an unlimited fashion, even if they seem to be very expensive
- GPL licensed code cannot be used
- LGPL licensed code may be used with specific approval on a case-by-case basis
- Mozilla licensed code cannot be used
- Apache licensed code can be used
- Anything else – ask first before using; Do not assume that is or is not OK to use; Please!
- Maintainability
Your work will be organized and documented well enough so that another person can maintain it with a minimum amount of effort.- This means that you must document APIs and their proper usage. Every class, function / method and variable must be commented in-code with sufficient detail so that I[1] can understand it.
- Dead code must be removed as soon as it is discovered.
- Clever algorithms and programming tricks are to be avoided.
- Write your code for readability, not for brevity.
- Writing code for performance overrides rules [c] and [d], but not [a]. In general the more “clever” you think your code is - the more documentation should be provided. When writing documentation, do assume that you are a genius and everyone else is retarded.
- The language in your comments, docs and sources must be free of profanity, racial slurs, anyone’s personal information or anything else that may make it unprintable to the general public;
- Use of Javadoc for Java is mandatory.
- Use of Doxygen for C++ is mandatory.
- Testability
- Every piece of your code must be instrumented for automation testing. This includes all UI elements.
- When a bug is fixed the test suite must be updated, explicitly identifying which test number is added to cover a specific bug number. You must make every effort to guarantee that when a bug is fixed, it will be impossible to re-introduce it later.
- I[2] must be able to open any source file, point to a function/method and, within the comments in the source, find out where the corresponding test(s), that cover that piece of code are.
- Performance
- All user actions are asynchronous – in other words the UI is NEVER blocked and waiting after a user action.
- All user actions are interruptible (browser style STOP)
- All user actions show a progress bar (browser style progress) (typically in window frame status area or some other less obtrusive UI element). The progress bar must correctly indicate percent completed, or processed item count vs. total item count / remaining item counts. It should appear to update logically and smoothly, rather than get stuck at 5% for 10 minutes, then jump to 90%. This may require a separate “total progress line” and a “current item progress” line.
- For those very rare user actions, for which it is not possible to know exactly what percentage of the work is completed, a progress bar will still be shown using an alternative “activity” visualization – for example a left-right “ping-pong” bar, or a round “ying-yang” spinner.
- System resource footprint configurability
- The user must have the ability to configure the maximum acceptable %CPU, max RAM, max HDD usage and HDD bandwidth and max network bandwidth, which the application is allowed to use.
- The user must have the ability to configure overall priority for the application, separately for foreground and background mode, and indicate if the app should back off more or less aggressively in each mode, so that other apps can execute.
- Undo and redo
- A full user action undo & redo is required and assumed to be standard for the application.
- Undo / Redo covers every possible user action, including UI layout changes, fonts changes, all configuration changes, text edits, data entry, etc.
- Drag and drop
- All data objects can be dragged and dropped to and from the OS context and other applications. This includes the “trash”, file folders, FTP transfer clients, editors, debuggers, PDF generators, etc. See “Actions on single items and collections” for additional requirements.
- Within the application, menu items can be dragged between menus and toolbar buttons between toolbars.
- In the application, highlighted text can be dragged and dropped between text entry fields.
- In the application, non-text data values, which may be modified using UI elements that have no text fields, can be dragged between the same type of entry area AND to and from text fields. For example a slider with a range of 1-100 can be dragged to a text field and when dropped it’s current value will appear in the field as a number.
- Actions on single items and collections
- If an action can be performed on a single item, then it must be possible to perform the action on a collection of items.
- This means that if one thing can be dragged and dropped – then MANY things can be multi-selected (Shift- or Ctrl- clicked) and dragged and dropped.
i. For example, dragging multiple items to a PDF generator icon will result in creating a single PDF, containing all items.
ii. For example, dragging multiple items to a ZIP icon will create a ZIP containing all items.
iii. For example, dragging multiple toolbar buttons from one toolbar to another is possible.
- Configurability
- All configurations are stored in a single XML file
- Parts of the XML file can and will be encrypted
- Everything is configurable – options, layouts, color schemes, skins, etc.
- The configuration file is the mechanism used to customize configurations for mass deployments – this is why portions of it may be encrypted or signed, so that the end users may not change certain subset of options, or never see a subset of features/menus/options.
- The application must be reconfigurable without requiring a restart.
- Configuration changes are saved as soon as they are made – not only when the user exists the application.
- The user interface for making configuration changes is implemented as a separate application, completely out of process of the main app. When changes are made a “reload configuration” message is sent to the main app, which then re-reads the configuration and reconfigures itself. The only exception is when the user makes UI layout changes within the windows of the main app, using drag & drop facilities.
- Menus, toolbars & shortcuts
- Menus can be reordered and menu items can be added and removed from menus and sub-menus;
- Items existing in one sub-menu maybe dragged to another submenu;
- Every menu name and every item on all menu levels have associated keyboard shortcuts (with standard visual letter underlining);
- In general the user must be able to use the application without a mouse;
- Toolbar buttons also have shortcuts and their buttons have versions for “Large Image”, “Small Image”, “Small Image & Text”, “Large Image & Text”, “Small Text Only”, “Large Text Only”, and the associated mouse-over, hover, grayed-out/disabled and other commonly needed states.
- Menu options and toolbar buttons are context sensitive and will be automatically grayed out, if they represent actions, which are invalid for the current user selection and/or application context.
- Toolbars and menus can be dragged to all four sides of the main application window or floated.
- Floated toolbars can be set to automatically render and align correctly on the right-left-top-or-bottom of the mail application window.
- Floated Toolbars have a transparency setting and a show/hide global shortcut;
- Context Menus (aka right-click in Windows)
- Help and balloons
- All help is context –driven.
In other words, pressing the help button (F1 in Windows) does not bring the standard Help Start Page, with an empty search box and a table of contents, but rather a context pre-searched result list of relevant topics. For example, if the user has selected some text, then right-clicked and opened a “Properties Menu” and then “asked for help – the results should be relevant to the possible actions offered by the dialog. This means that every UI object must carry a context help search string. This also means that when help is requested a “logical OR” concatenation of these context help strings is submitted to the help search engine. Finally, these context help search strings must be externalized in the same manner as the UI texts for localization purposes. - Hierarchical network enabled help.
The typical desktop application carries a local copy of the help files, which in many cases is backed up by a (usually) more current web help system. A good help system should have multiple network based help server URLs registered for it. This allows corporate users to have a locally updated help server for the application, with additional custom content, so that in a large organization desktop help queries do not propagate to the outside internet, unless the in-house server is unavailable. Updates from the vendor are broadcast to the local help server and the desktops pick up the help updates locally. Of course, if the local help server is not available, the vendor one is automatically accessed. In terms of incrementing a desktop application to handle these use cases, it is only necessary to have the ability to register a prioritized list of URLs, using a “help://” protocol nomenclature. - Balloon help must be available for all UI elements. User-configurable options for balloon show/hide timings must be present.
- All help is context –driven.
- Saved file versioning
- If the application stores data (most do), hen by default at least one previous version will be retained.
- The application will offer the user a configurable option to store a copy of the information they are working with automatically every “X” minutes.
- The application stores by default archive (revertible-to) versions of previous configurations. An archive configuration version is stored when the application shuts down, if it has changed from the previous version.
- Localization and Internationalization
- By default the application supports Unicode strings;
- The application reads and writes UTF-8 formats; It’s configuration files are UTF-8 encoded XML;
- The application externalizes all UI strings and provides a text-file based translation facility, so that additional languages can be added by the “end user”, if necessary. This includes externalizing the formats for date/time and money.
- The application makes provisions in its UI layout management for right-to-left and top-down languages.
- When designing UI elements with text labels in English, add 35% extra width and 20% extra height to all containers to accommodate internationalized version;
- Always use a layout manager or proportional spacing for UI elements. Never hardcode x-y UI element sizes – they must all be calculated as a proportion of the main window (form) and optionally with respect to the user’s screen resolution.
- Absolute conflict prevention
- Multiple versions of the application can be executed at the same time on the same system without conflicts.
- Multiple instances of the application can be executed on the same system – this should be a user-accessible option in the command line when launching the app.
- If the application provides some of its sub-components as 3rd party SDKs, and 3rd parties have used them to create their own applications or application extensions, then there should be no way a 3rd party app can create a conflict with the “native” application. This is a specific concern when using Windows COM and dealing with “DLL hell”.
- See “install by copy as a related requirement”.
- Install by copy
- The Windows Registry is a bad bad BAD thing – we do not use it unless we absolutely have to[3].
- Installation should be a mater of simply copying all of the application’s code to a directory and running the main executable from it.
- As a corollary – install by copy means that the application can run from read-only media – like a CD, without an install.
- It is sometimes necessary to obtain a “windows compatible” logo or certification and this requires that executables, shared libraries, user data and registry entries be placed in specific subdirectories on the user’s computer. The application should be a well behaved citizen of the native OS as much as possible, while sticking to the above requirements.
- Accessibility
- This is one of the few optional sections. Accessibility for people with disabilities is relevant only if the software is targeted to the public sector. In general use this rule of thumb – if you are not going to ship more than 100,000 copies to state governments for use in educational programs, you probably do not have to worry about this section.
- An optional TTS mode would be available, in which content under the cursor is read through the computer speaker.
- An optional Large Font UI mode would be available, in which content under the cursor is magnified up to 10x.
- An optional “magnifier cursor” would be available to enlarge application UI;
- Startup and Shutdown
- The application absolutely must launch in under 3 seconds – this means that no matter what data the app must load form disk or connect to – after 3 seconds the user can start interacting with it’s UI or switch to another application. The OS and user must not be blocked for more than 3 seconds under ANY circumstance.
- The application absolutely must shut itself down in 3 seconds or less. The Windows OS message of “Cannot End Application – click here to End” is absolutely verboten.
- Exception Handling and Error Reporting
- The application should be “un-crash-able” – in other words, as much as possible, every function call should be in a try-catch block, with the assumption that an exception would be thrown there and must be handled.
- All caught exceptions will be written in a crash.log file, identifying the time, function name, arguments, app version, user id, etc.
- The application will be able to send these crash.log files to the vendor’s (this means us) QA server(s). In general, when the user is in trial mode or uses the app for free this info is sent automatically, when the user becomes a paying customer, they have the option of turning this off. The crash.log is used to debug user problems.
- Usage pattern and Usability statistics
- The application will log usage patterns – aka click paths in order to provide the usability team quantitative data.
- The application will log user actions and their timing. In other words, the log format will have a timestamp + user action on each line.
- Like the crash.log data, free/eval copies will submit this info automatically, while paid users can turn it off.
Comments