Posts in "Pragmatic programming" category

Characteristics of "Great Software Design"TM

This post is not about design-design, but the internal design of a software system. Such a design has several general characteristics, and Code Complete 2 has a list of these. First, a related quote by R. Buckminster Fuller:

“When I am working on a problem I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong.”

Great Software Design would have all these characteristics, but that’s almost never possible. Some of them contradict each other, so often we have to make tradeoffs. Anyway, here’s the list:

  • Minimal complexity – if your design doesn’t let you safely ignore most other parts of the program when you’re immersed in one specific part, the design isn’t doing its job. This is also known as avoiding complexity.
  • Ease if maintenance – design the system to be self-explanatory. Wheter it’s yourself or a maintainance programmer that’s going to sit down and fix a bug in a couple of months time it’ll be worth it.
  • Loose coupling – Loose coupling means designing so that you hold connections among different parts of a program to a minimum. This means; encapsulation, and information hiding [read] and good abstractions in class interfaces. This also makes the stuff easier to test, which is a Good Thing.
  • Extensibility – You can change a piece of the system without affecting other pieces.
  • Reusability – Designing the system so that you can use pieces of it in other systems.
  • High fan-in – This refers to having a high number of classes that use a given class. This is good, the opposite on the other hand …
  • Low-to-medium fan-out – Refers to how many classes a given class use. If a class have a high fan-out (Code Complete says this is more than 7 classes, I don’t want to be that specific) this is often an indication of that the class may be overly complex. And complexity is bad, remember?
  • Portability – How easy would it be to move the system to another environment?
  • Leanness – I guess this could be compared to KISS. Voiltaire said that a book is finished not when nothing more can be added but when nothing more can be taken away. Extra code will have to be developed, reviewed, tested, and considered when the other code is modified.
  • Stratification – designing “in layers”. Can you view “one layer” of the code without thinking about the underlying layer? An example giving in Code Complete is if you’re writing a modern system that has to use a lot of older, poorly designed code – you would want to write a layer of the new system that is responsible for interfacing with the old code.
  • Standard techniques – this means using design patterns whenever it is appropiate to do so. This way, if you say to another coder “Here I use the Factory pattern” he will instantly know what you’re talking about if he knows the pattern. You do not want to be one of those “valued employees” who only write code that the “valued employee” can understand.

And, whatever you do: DO NOT CONSIDER SPEED! NEVER OPTIMIZE WHILE DESIGNING! (yes, those really needed to be capitalized). If I have one more PHP coder tell me that the MVC solution probably won’t be speedy, I will have to slashdot him physically in public. You don’t want to start off by trading any of the characteristics above for speed.

You design the system with design in mind, and if it turns out to be slow then you optimize. You will know what the bottleneck is, and you will make a sensible and thought-through trade-off. Then you will have your good design, and your speed. You will, rule the entire planet. Hah, kidding, the last part I just made up.

Well, what are you waiting for? Run out in a flowery meadow with your pen and paper and design something beautiful!

Cutting the Gordian Knot

“Gordius, the King of Phrygia, once tied a knot that no one could untie. It was said that he who solved the riddle of the Gordian Knot would rule all of Asia. So along comes Alexander the Great, who chops the knot to bits with his sword. Just a little different interpretation of the requirements, that’s all… and he did end up ruling most of Asia.”

So begins chapter seven of one of my favourite programming books of all time.

Finding the answers to impossible problems is all about asking the right questions. So, here you have them ;-)

Problem solving questions

  • Is there an easier way?
  • Are you trying to solve the right problem, or have you been distracted by a peripheral technicality?
  • Why is this ting a problem?
  • What is it that’s making it so hard to solve?
  • Does it have to be done this way?
  • Does it have to be done at all?

TPP promises; “Many times a surprising revelation will come to you as you try to answer one of these questions.”

One of the original books on heuristics in problem-solving was G. Polya’s “How to Solve It” (1957). Although his book is about problem solving in mathematics, it can just as well be used for software engineering.

Here’s a summary of Polya’s problem solving approach:

    1. Understanding the problem. You have to understand the problem. What is the unknown? What are the data? What is the condition? Is it possible to satisy the condition? Is the condition sufficient to determine the unknown? Or is it insufficient? Or redundant? Or contradictory? Draw a figure. Introduce suitable notation. Separate the various parts of the condition. Can you write them down?
    2. Devising a Plan. Find the connection between the data and the unknown. You might be obliged to consider auxiliary problems if you can’t find and intermediate connection. You should eventually come up with a plan of the solution.     Have you seen the problem before? Or have you seen the same problem in a slightly different form? Do you know a related problem? Do you know a theorem that could be useful?     Look at the unknown! And try to think of a familiar problem having the same or a similar unkown. Here is a problem related to yours and solved before. Can you use it? Can you use its result? Can you use its method? Should you introduce some auxiliary element in order to make its use possible?     Can you restate the problem? Can you restate it still differently? Go back to definitions.   If you cannot solve the proposed problem, try to solve some related problem first. Can you imagine a more accessible related problem? A more general problem? A more special problem? An analogous problem? Can you solve a part of the problem? Keep only a part of the condition, drop the other part; how far is the unknown then determined, how can it vary? Can you derive something useful from the data? Can you think of other data appropriate for determining the unknown? Can you change the unknown or the data, or both if necessary, so that the new unknown and the new data are nearer to each other?     Did you use all the data? Did you use the whole condition? Have you taking into account all the essential notions involved in the problem?
    3. Carrying out the Plan. Carry out your plan. Carrying out your plan of the solution, check each step. Can you see clearly that the step is correct? Can you prove that it’s correct?
    4. Looking back. Examine the solution. Can you check the result? Can you check the argument? Can you derive the result differently? Can you see it at a glance?     Can you use the result, or the method, for some other problem?

    Woh! That’s a lot of problem-solving questions for you – it’s almost like finding the “unsub” in an episode of the CBS show Criminal Minds isn’t it?

    Also, notice how similar this is to design patterns? Those problem solving questions are almost like a design pattern for finding solutions! Go ahead – print them out, and refer to them when needed.

Code checklists

The best things in life are free smart and simple! As code checklists for example. I’m currently reading Bob Walsh‘s recent book Mirco-ISV – From Vision To Reality, which is a book about running your own Micro-ISV (independent/internet software vendor). Micro-ISV’s are often one-man companies, for example single developers producing and selling their own software. Early in the book Walsh talks about ‘developing the Micro-ISV way’, and this is where he gets into code checklists:

Using a code checklist is the easiest method of the lot. Go into Microsoft Word, write the half-dozen coding mistakes you’ve last made, print the document, and look at the list as you review the code you wrote today. See any familiar faces? Write down other ways you miss the mark as they crop up, and revise your code’s checklist.doc periodically. What you’ll notice over time is coding is just like spelling. You make certain coding mistakes repeatedly, but if you focus on them, you’ll make them less often. Just the process of checking my code against my private coding checklist identifies about half of the coding mistakes I make, especially those nasty increment-by-zero blunders and other simple errors. Give it a try!

Strange I haven’t thought about this before, or done it before – but I haven’t. At least not that I can remember. I’ve just started doing it, so I don’t know how much it will pay off, but it looks like a good idea at least :) .. I’ve decided to use one checklist for each language I code in (e.g. java.txt, php.txt, javascript.txt, etc.) – as I code in multiple languages each day. In addition I think it could be a good idea to include functions and things I often forget that I have to look up way too often (like various string functions in JavaScript!).