Monday, 29 July 2013
Firstly I'd like to thank Jason Butler. You taught me an important lesson very early on. Jason and I worked together as interns (as close a term as I can work out) while at university. Jason taught me me this:
Just because someone talks slowly, doesn't mean that they think slowly.
I'd like to thank Jason Ngaio for my first real exposure to C++. Jason was the instructor of the C++ course that my first employers sent me on. This was my first real job, and the first time that I think I really got object oriented programming.
I'd like to thank Derrick and Pam Finlayson, Arran Finlayson, Blair Crookston, Jenny Cohen, Mathew Downes and Rachel Saunders. You guys helped me develop personally. The confidence and people skills that I learnt while around you has undoubtedly helped me in my professional career in software development.
David Cittadini from the then Sapphire Technology company based in Wellington really expanded my vision and understanding of developing complex systems. David also got me back into reading around the programming topic. My technical library started there. Working with Chris Double helped me understand what it is like to work with someone else in synergy. Our joint output I'm sure was a lot more than what we would have both produced independently added together.
David Ewing made a significant impression on me around knowing my worth and helped in contract negotiations. David has a wonderful way of dealing with people.
Moving over to London gave me the opportunity to meet up with some truly awesome people. Getting involved with ACCU was great for me. I worked briefly with Allan Kelly at Reuters, but learned a lot in a brief time. I also had the opportunity to work with Giovanni Asproni and Alan Griffiths at Barclays Capital. Working with you two really helped me understand the power that the developers hold when talking to the business. A few other people I'd like to make a personal note of from this time in the UK are Kevlin Henney, Roger Orr and Pete Goodliffe.
From my early time at Canonical, I'd like to personally thank Jonathan Lange, Robert Collins and Michael Hudson-Doyle. You guys really helped me understand the importance of writing good tests, and test driven development. Also the hammering in the code reviews teaching me how to write those tests well.
There are so many other people that I have had great connections with over my professional career and I'd like to thank you all. Work is more than just what you produce, but the friendships and connections you make with the people you are creating things with.
Wednesday, 17 July 2013
The original working title for this post was "Go is hostile to developers". This was named at a time of extreme frustration, and it didn't quite seem right in the cooler light of days later. Instead I've settled on the term "stunned", because I really was. I felt like the built-in standard library had really let me down.
Let's take a small step back in time to the end of last week as I was debugging a problem. In our codebase, we had an open file that we would read from, seek back to the start, and re-read, sometimes several times. This file was passed as an io.Reader into another of our interfaces which had a Put method. This stored the content of the io.Reader in a remote location. I was getting this succeeding the first time, but then erroring out with "bad file descriptor".
The more irritating bit was that the same code worked perfectly as expected with one of our interface implementations but not another. The one that failed was our "simple" one. All it used was the built-in http library to serve a directory using GET and PUT http commands.
@TheMue suggested that our simple storage provider must be closing the file somehow. Some digging ensued. What I found had me a little exasperated. The standard http library was calling Close on my io.Reader. This is not expected behaviour when the interface clearly just takes an io.Reader (which exposes one and only one method Read).
This clearly breaks the "Principle of Least Astonishment"
People are part of the system. The design should match the user's experience, expectations, and mental models.
Developers and maintainers are users of the development language. As an experienced developer, it is my expectation that if a method says it takes an interface that exposes only Read, then only Read will be called. This is not the case in Go standard library.
While I have found just one case, I have been informed that this is common in Go, and that interfaces are just a "minimum" requirement.
@howbazaar you'll also find it's pretty common. io.Copy() will call ReadFrom and WriteTo methods, WriteString() is called to avoid copy.— Jesse McNelis (@jessemcnelis) July 13, 2013
It seems to me that Go uses the interface casting mechanism as a way to allow the function implementation to see if the underlying structure supports other methods, or to check for actual concrete implementation types so the function can take advantage of extra knowledge. It is one thing to call methods that don't modify state, however calling a mutating function that the original function did not express an intent to call is so much more than just unexpected, but astonishing.
I found myself asking the question "Why do they do this?" The answer I came up with two things:
- To take advantage of extra information about the underlying object to make the execution of the function more efficient
- To work around the lack of function overloading
Now the second point is tightly coupled to the first point, because if there was function overloading, then you could clearly have another function that took a ReaderCloser and it would be clear that the Close method may well be called.
My fundamental issue here is that the contract between the function and the caller has been broken. There was not even any documentation to suggest that the contract may be broken. In this case, the calling of the Close method on my io.Reader broke our code in unexpected ways. As a language that is supposed to be used for systems programming, this just seems crazy.