DocumentationA README file in the source is not sufficient for the end-user. Good quality documentation must explain how to use all the key features in a coherent manner. For example, a command line tool should be at least shipped with a man page, and this must cover all the options in the tool and explain clearly how each option works.
Documentation must be kept up to date with the features in the software. If a feature is added to a program and the documentation has not been updated then the developer has been careless and this is a bug. Out of date documentation that provides wrong and useless information make a user lose faith in the tool.
Try to include worked examples whenever possible to provide clarity. A user may look at the man page, see a gazillion options and get lost in how to use a tool for the simple use-case. So provide an example on how to get a user up and running with the simple use-case and also expand on this for the more complex use cases. Don't leave the user guessing.
If there are known bugs, document them. At least be honest with the user and let them know that there are issues that are being worked on rather than let them discover it by chance.
Expect quality when developingDon't ship code that is fragile and easily breaks. That puts users off and they will never come back to it. There really is no excuse for shipping poor quality code when there are so many useful tools that can be used to improve quality with little or no extra cost.
For example, develop new applications with pedantic options enabled, such as gcc's -Wall -Wextra options. And check the code for memory leaks with tools such as valgrind and gcc's mudflap build flags.
Use static code analysis with tools such as smatch or Coverity Scan. Ensure code is reviewed frequently and pedantically. Humans make mistakes, so the more eyes that can review code the better. Good reviewers impart their wisdom and make the process a valuable learning process.
Be vigilant with all buffer allocations and inputs. Use gcc's -fstack-protector to check for buffer overflows and tools such as ElectricFence. Just set the bar high and don't cut corners.
Write tests. Test early and often. Fix bugs before adding more functionality. Fixing bugs can be harder work than adding new shiny features which is why one needs to resist this temptation and focus on putting fixes first over features.
Ensure any input data can't break the application. In a previous role I analysed an MPEG2 decoder and manually examined every possible input pattern in the bitstream parsing to see if I could break the parser with bad data. It took time, it required considerable effort, but it ended up being a solid piece of code. Unsanitised input can break code, so be careful and meticulous.
Sane DefaultsPrograms have many options, and the default modus operandi of any tool should be sane and reasonable. If the user is having to specify lots of extra command line options to make it do the simplest of tasks then they will get frustrated and give up.
I've used tools where one has to set a whole bunch of obtuse environment variables just to make it do the simplest thing. Avoid this. If one has to do this, then please document it early on and not buried at the end of the documentation.
Don't break backward compatibilityAdding new features is a great way to continually improve a tool but don't break backward compatibility. A command line option that changes meaning or disappears between releases is really not helpful. Sometimes code can no longer be supported and a re-write is required. However, avoid rolling out replacement code that drops useful and well used features that the user expects.
The list could go on, but I believe that if one strives for quality one will achieve it. Let's strive to make software not just great, but excellent!