I was reading a blog post by one of my favourite authors talking about how difficult he finds it to hand in a manuscript.
"It’s a hard thing for me, letting go of a manuscript. Other authors are very matter of fact about it. But for me, obsessive word tinker than I am, I hate knowing that after a certain point I won’t be able to go in and fix things any more."
I feel something very similar when I'm coming to the end of a milestone and the code I've been working on is getting close to "complete". Because I am paid to create functionality and not art, it's actually possible for me to over polish the code. So I thought I would write about what goes through my head when I'm trying to decide if my code is ok. Obviously, the code has to meet the written requirements - otherwise it is deemed "not good enough" by QA.
Meeting the written requirements
- Obviously, the software I write has to meet the immediate functional requirements - it has to work as intended.
- It, usually, also has to meet the design requirements - it has to look right.
- There are also user experience requirements - it has to feel right.
However, the written requirements tend to deal with the tangible parts of the software. It now becomes a judgement call on the part of the developer whether the code she has written meets the required standards for the unwritten requirements - things like maintainability, readability, reusability, extensibility, architecture and test quality. How important these requirements are depend very much on what you are building and the time frame your are working in.
I genuinely believe there is more value in a developer who develops good quality software quickly, than code poetry slowly. I justify this by saying that after a certain level of quality in a piece of software the effort starts to out weigh the gain and the reason for putting the effort in becomes less about the software and more about developer ego. I think the developer who understands this and judges correctly when to stop is the better craftsman.
Meeting the unwritten requirements
Software is constantly evolving and it's sometimes not possible to come up with the "best" solution for a piece of code until after it's been in use for a while. We were seeing lots of forking in the code where data needed to be processed differently depending on certain circumstances, so we decided to write a more elegant method of dealing with this. I closed the code review in which the final outcome of the discussion was to leave the code unfinished and revisit it to see how it's actually being used. The more time that has past, the more I think that turned out to be a really good idea. We waited to see how other developers would need to use it and how some might try and abuse it. The solution we have now makes the common forms of abuse much more difficult, while aiding the current common usages.
The above is, in my opinion, an example of where good quality code really shines. The code doesn't just perform it's own task, it creates a process for other developers to quickly perform similar tasks. When given a new task, most developers will look at the system and see if something similar has been done before. At some point, your code will likely be used as a base for something similar, or a small part of it will be used as the pattern for "doing x" in the system. You can aid in this by making the code easy to read and understand. If it's loosely coupled, it'll be easier for other devs to work into something new and that new thing will likewise be loosely coupled.
I am a big fan of using tests to help other developers understand the subtlety of the requirements of my code. If I make an assumption during the development of some code and it turns out to be wrong, that's a test right there. If a future dev makes the same assumption and introduces a bug because my tests didn't fail - that bug is on me.
When trying to decide if my code is "good enough" yet, here's what usually goes through my head:
- Would I be ok seeing this code replicated through the system.
- Is this problem local to this code, or do I need a global solution for other devs to use.
- Is this problem properly tested.
- Can I justify changing this?
No comments:
Post a Comment