Useful, efficient software matches the user's needs, knowledge and skill. Don't buy a swiss army knife when you need a wrench...

About Us

A young company still hungry for challenges, with 30+ years of experience with software development from small embedded systems to distributed real time PC networks and SQL databases. A reasonable design effort to deliver high-quality software e.g. for medical devices on schedule.

External links

    Customers

 

 

     Other sites

 

These criteria are by no means complete, but rather work in progress. It aims to see software both from the user's and the developer's perspective.

 

0. Necessity

Does this software really need to be developed?

Does it fulfill a need that no existing software or manual methods solve as well or better?

 

1. Core functionality

What is the actual problem we are trying to solve? Usually this has nothing to do with programming - it might be to get some data from a source and put it at a destination, possibly transformed in some way. E.g to take pictures form a digital camera and put thumbnails on a web page or other UI.

Sometimes there may be more than one core functionality. It might be worthwhile to consider two separate, simpler programs than one complex one. It depends on how it will be used.

 

2. Think deep, design flat - UI

Users should dig the UI, not dig into the UI... Menus are fine, sub-menus are tedious, sub-sub-menus just frustrating. No matter how logical, a user that just dug into a sub-sub-menu, canceled an operation and have to browse down to the sub-sub-menu again will be frustrated.

 

3. Intended user

Consider the intended user - is he or she using the software on an everyday basis ore only a few times in a year? An untrained user would need guidance through the system through series of dialogs or wizards presenting only the current choice whereas an expert user would probably want all (common) functions reachable instantly.

For expert users tab-order and accelerators are important. Even old-fashioned module codes or a command line interface may improve productivity, and the software shouldn't require too many switches between mouse and keyboard entry.

 

4. Think deep, design flat - code

The same applies to code. Code is (should be) written for humans to read. Not for computers to compile.

There is a tendency to require a number of architectural layers in software, e.g. UI - Presentation - Service - Data objects - Data access. This is fine and a standardized architecture makes it easy to browse an unknown application, but for a single window application performing a relatively specialized task it may be more readable to put everything in code behind the window. In my opinion it is easier to read one five-line function in one class than to read five one-line functions calling each other in five different classes.

There are of course other benefits of separating UI from model like testability and the ability to swap one implementation against a different one, but honestly: how often do you replace the UI only in an application? Or the database for that matter?

 

5. Performance

There are really two kinds of performance: The batch-perfomance and the responsiveness of the application. Batch performance is less crucial - it can always be accepted if the program just estimates "I'll be busy for two hous now...". Of course one hour is better but the user can still be happy with two hours.

Responsiveness is crucial - Users clicking a button and nothing appears to happen so the user clicks a few extra times just in case, and possibly kills the application from the task manager. I'd say this is one major source of software crashes, corrupted data and user frustration.

IMO a performance improvement by a factor of 2 is noticeable, a factor of 5 is worthwhile. This is when I buy a new computer ;-). Hunting 30% improvement isn't worth the time spent.

Largest performance improvement is usually achieved by changing algorithm. Replacing Bubblesort (O(n^2)) with Heapsort (O(n log n)) or searching binary (O(log n)) instead of linearly (O(n)) can change your application from sluggish to blindingly fast for large data sets. Optimizing hte operation (e.g. comparsion) in the inner loops usually gains much less.

 

6. Reengineering

Basically, reengineering is good. Replace incomprehensible, inefficient code with readable, efficient code. Be careful though if the old incomprehensible, inefficient code works. It may be incomprehensible just because the problem is complicated. Don't touch until you understand it.

Also be careful rewriting everything from scratch. Many companies have gone out of business because they threw out an old program to write a new one from scratch. Old code is usually very well tested, whereas new code tend to introduce lots of new bugs that eventually will cause trouble for the customers.

Luckily rengineering is most frequent with new code that needs stablization, and old stable code is (hopefully) pretty maintainable.

 

7. 10.0 * 0.1 is hardly ever 1

Floats are always approximate. Don't use them for equality comparsions, or rather not for comparsions at all. For scientific calculations they are excellent but for anything that you rely on exact values they are poor or even dangerous. Floats will always be rounded at some point - make sure you know where and how.

 

8. Naming

Selecting good names for entities in the program is crucial for comprehension. A WinForm application using referring to Text1.Text is the signature of an amateur. Here are some guidelines tha tof course are breakable if it makes code clearer:

1. Use a convention for naming. E.g. Capitalization (Pascal-case) for functions, camel case for variables, I prepended to interfaces, ...

2. Be as specific as possible, e.g. "ParameterlessCommand" is better than "SimpleCommand" (if this is the intention).

3. Avoid abbreviations unless they improve readability. An 80 character identifier may improve by abbreviating it. On the other hand there may be a good, shorter and as suitable identifier...

4. Use conventions consistent with existing code (if there is some), or common naming conventions for the implementation language.

5. I am not too fond of names indicating type or role like "CancelButton" or "DiagramViewModel". I would rather separate ViewModels from Views and Models by namespace instead of naming conventions but this is merely personal taste and the other method appears to be common practice.

One observation: Microsoft now recommends against Hungarian notation, which has lead developers to use postfix naming conventions instead of prefix (e.g. OKButton instead of btnOK). Much better indeed ;-). And of course Microsoft breaks their own recommendation using I as prefx for interfaces...

 

9. Divide and Conquer

I'd rather say Conquer or divide. As long as you easily grasp the code you don't need to divide it. There is an old rule that says that a method should be between 5 and 50 lines. More fractioned code reduces readability because you need to gather source code from many different sources and merge them in your head. Longer methods tend to be too complex to comprehend.

But of course this is no unbreakable rule - just a hint on readability. Complex expressions may gain clarity if broken down to smaller one-line expressions.

 

10. Don't save code you are unsure of

The most common bug historically is the "off by one bug", i.e. looping 9 or 11 times when there should be 10 or similar. My guess is that the programmer did think about it ("now should this be 0 to 10 or 1 to 10") but decided to figure that out later. Complexity to find bugs increase exponentially with the number of bugs, so saving one bug may double the time spent debugging later on. Make sure your code works already when you are designing it.Also never fix a bug by compensating for it at some other place. The key to solve a problem is to understand the problem. "It works" means just "It works for the moment but may break in the future".

Copyright © 2013 Useful Software Sweden AB E-mail: info@usefulsoftware.se, Phone: +46 708 727662 
Useful Software Sweden AB