Friday, November 27, 2009

Four Bits Worth of Dev Advice: Last Half a Bit

This the 5th and final post in my series about my 16 pieces of developer advice. In the previous post I listed the 16 pieces in headline form ([1]), and briefly explained the first 12 pieces ([2], [3], [4]). In this post I go through the last four pieces. Enjoy!

1100: Write change friendly code
This piece of advice is about a mind set: Keep in mind that the code your writing will most likely be changed a lot of times in the future. Therefore you need to write code that can be changed without too much pain. This mind set leads to a lot of the common coding best practices like using symbolic constant, like avoid unnecessary dependencies, like keeping functions short and the list goes on and on. These things brings readability to the code, and they enable changing the code later on. On the other hand you should (of course) avoid over-designing software, so this nugget is really mainly about the small code level things. And those small code level things make a huge difference when it comes to the modifiability of software.

1101: Dry
Don't repeat yourself. Or DRY. This piece of advice has been re-iterated so much, that I will leave at a reference for you to follow, the C2 wiki entry on DRY.

1110: Don’t stay stuck
You sometimes get stuck when coding, in fact some days coding feels like this:

Getting into situation where you are stuck and can't see what the next step is, is normal and something you need to accept as part what it's like to write code. The important part is what you do about it. Some developers start thrashing; they run around in circles without really getting anywhere. Others start surfing for cartoons (or ... well, you get the idea). That doesn't help. To get out the situation your stuck in you probably need help - I know, I usually do - so seek help. Ask a team mate to pair with you. Ask your project manager for that tool you need. Ask a senior colleague to take a look at your problem. Read up on the relevant literature. Just don't stay stuck.

1111: Be curious
The final piece of advice on the list is simply to be curious in your work. And use that curiosity to gain technical insight. For instance ask yourself how the frameworks or libraries you use are implemented. Maybe read some of the code, step through it in a debugger, or read the documentation (yes, it said read the documentation :-) ). Or take a look at the code your compile generates, be assembler, bytecode, MSIL or something else. That generated code can tell you a lot about how the platform and language you're in actually works. Just try to stay curious about how things actually works, and spend a bit of time and then satisfying that curiosity.


So that was my list of 16 pieces of developer advice. I had fun compiling the list. I started out with a much longer list, but it was fun to boil it down to 16. What do you think is missing from the list, and do you think should be taken out?

Friday, November 13, 2009

Four Bits Worth of Dev Advice: Half a Bit More

This is the fourth post in my series of four bits worth of dev advice the first three ([1], [2], [3]) covered the sixteen pieces of advice in headline form and the first eight pieces in a bit more detail. This post goes through another four pieces of advice.

1000: Use symmetric interfaces
When designing an interface we have to decide which methods the interface contains. If you know exactly how the interface will be used - e.g. you're the guy developing the code that uses the interface - deciding what's in an what's out of the interface is pretty straight forward. If, on the other hand the interface will be used in unknown ways - e.g. you're developing a library that others will use - the decisions are harder. In the later case symmetry can be an excellent guide: If an interface has method A that has a logical inverse operation B, the interface should probably have B method. For instance, consider this code from the Rotor project:


class CClassFactory :  public IClassFactory
{
CClassFactory() { }

public:
CClassFactory(const COCLASS_REGISTER *pCoClass) : m_cRef(1),m_pCoClass(pCoClass)
{ }

// snip

virtual HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid,
void **ppvObject);

// snip

private:
LONG m_cRef;
const COCLASS_REGISTER *m_pCoClass;
};


This is a typical factory: It can instantiate a certain type of object through the CreateInstance method. Since this is C++ those objects have to be deleted (or at least logically freed, if some sort of pooling is in place) at some stage. But the factory interface does not help us there, so we can only guess how deletion is supposed to be done. This is a case where introducing the symmetric method DestroyInstance might make sense. That makes the code look like this:


class CClassFactory :  public IClassFactory
{
CClassFactory() { }

public:
CClassFactory(const COCLASS_REGISTER *pCoClass) : m_cRef(1),m_pCoClass(pCoClass)
{ }

// snip

virtual HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid,
void **ppvObject);

virtual HRESULT STDMETHODCALLTYPE DestroyInstance(void *ppvObject);

private:
LONG m_cRef;
const COCLASS_REGISTER *m_pCoClass;
};

Adding symmetric operations to interfaces is not a fast rule. That DestroyInstance method might not be necesarry in the case above - I haven't diged deeply into the Rotor code, so I don't know. But if you're in doubt, and including the symmetric operation(s) doesn't seem un-economic, make the interface symmetric.

1001: Use one abstraction level per method
This a classic from Martin Fowlers Refactoring book: In order to write nice readable methods use only one level of abstraction within any given method. If a method contains some code that operates on a higher level of abstraction, and some code that operates on a lower level of abstraction, the lower level code should be extracted to a separate method.
Operating on only one level of abstraction in each method allows the reader to stay on that level of abstraction when reading the method, which is a lot easier than mentally jumping between levels of abstraction. Furthermore the reader knows that he is going down to a lower level of abstraction when going into the implementation of a method, B, which called from another method, A.
To illustrate take a look at this lovely piece of (in-production) Java code:


private void init() {
try {
server = new ServerSocket(Integer.parseInt(PersistentData.getStringValue(PersistentData.UDP_PORT_MLPI,
PersistentData.UDP_PORT_MLPI_DEFAULT_VALUE)));

javavendor = System.getProperty("java.vendor");
System.out.println("Java vendor: " + javavendor);
if (javavendor.startsWith("Sun ")) {
m_DatagramThread = new
DnDatagramThread("localhost",Integer.parseInt(PersistentData.getStringValue(PersistentData.UDP_PORT_WCTRL,
PersistentData.UDP_PORT_WCTRL_DEFAULT_VALUE)));
}
//snip
} catch (java.net.BindException e) {
System.err.println("A previous instance is already running...." + "\nCannot run application: " + e.getMessage() + "\n" + e);
System.exit(1);
} catch (final java.io.IOException e) { // an unexpected exception occurred
System.err.println("Cannot run application: " + e.getMessage() + "\n" + e);
System.exit(1);
}

m_properties = new HashMap();
m_splashScreen = new SplashScreen();
ErrorDialog.instance().setInitialWindow(m_splashScreen);
ErrorDialogCheck.instance().setInitialWindow(m_splashScreen);

progress(5);
setupDebug();
progress(10);
}
This code opens a socket - one level of abstration - then creates and sends a datagram over that socket - another, lower, level of abstraction - and goes on to display a splash screen - a third, higher level of abstraction. Having those three levels of abstraction means that the reader has to think about sockets, protocol specifics and GUI simultaneously to grasp what is going on in the code. That's not separation of concerns. That's spaghetti.

1010: Grow GUTs
Grow some GUTs while developing software. That is grow some Good Unit Tests. Good unit tests express and test the intended functionality of the code under test. It covers not only the different paths through the code, but all the requirements that the code under test is supposed to fulfill.
Most of the unit tests I see revolve around the implementation details of the code under test rather than the actual functionality. Lots of test suites are build by creating one test method per public method in the code under test. Each of these test methods try to cover one method from the code under test. This usually means that most of the tests only check very superficial things, and then one or two of the test methods test an awful lot of things all at once because try to cover the one or two central public method of the code under test. These large test methods become unwieldy, while still not covering the entire functionality of the code under test. Basically the public interface of the code under test is bad unit test template - a BUTT. And who wouldn't rather grow their GUTs than their BUTT?
For a thorough write-up on good unit tests I recommend Kevlin Henneys StickyMinds series ([1], [2]) on the subject.


1011: Prefer use over reuse
Writing re-usable software is something lots of developers want to do. Writing re-usable software is also really hard. In fact, writing usable is hard. And if software isn't even used it certainly won't be reused.
Moreover aiming for reuse in the first iteration over a software design tends to make the design bloated and unclear (i.e. un-economic), because the requirements to the software are unclear, and because there are multiple scenarios in play that the design tries to accommodate. That sort of bloated software will just end up sitting unused on your hard drive. To counter that, focus on one scenario that you're sure of. Prefer to make something that is used in just that one scenario. Then go on to build something that is used in one other scenario. Then go on to build something that is used in a third scenario. Only then, after a number of instances of use, think about reuse.

That's it for now.