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".
|