November 20, 2013
Retro ProgrammingAfter being reminded of some key information sources that pre-date the year I was born (1971, if you please, and not 10,000 BC as some have suggested), I've set myself a little challenge. Well, it's good to have a hobby, right?
I've long known that many of the "new" practices I use as a software developer are actually not as new as people think. In fact, some date back - in one form or another - to the 1950's.
Take Test-driven Development, for example. Kent Beck spoke of how he "rediscovered" TDD, referencing a book - whose name he appears to have forgotten - that spelled out how programmers could define a program by examples of inputs and outputs (in this case recorded on magnetic tape) and writing code to map the input onto the output. That's TDD.
Other TDD-like references date as far back as 1957 (and lets assume that the actual application of those practices goes back even further). From Arialdo Martini's blog post "You won't believe how old TDD is ", he quotes from the book "Digital Computer Programming" by Daniel D McCracken:
"The first attack on the checkout problem may be made before coding is begun. In order to fully ascertain the accuracy of the answers, it is necessary to have a hand-calculated. check case with which to compare the answers which will later be calculated by the machine. This means that stored program machines are never used for a true one-shot problem. There must always be an element of iteration to make it pay. The hand calculations can be done at any point during programming. Frequently, however, computers are operated by computing experts to prepare the problems as a service for engineers or scientists. In these cases it is highly desirable that the “customer” prepare the check case, largely because logical errors and misunderstandings between the programmer and customer may be pointed out by such procedure. If the customer is to prepare the test solution is best for him to start well in advance of actual checkout, since for any sizable problem it will take several days or weeks to and calculate the test."
What jumps out at me from this paragraph is the allusion to an iterative process of programming, driven by tests that are written by the customer (a domain expert).
Similar references to TDD-like practices can be found in interviews with Jerry Weinberg where he talks about his experiences on NASA's Project Mercury in the early 1960s. It was also stated by Craig Larman that development on Project Mercury was done "top-down" (we now call it "outside-in") using test doubles.
So we can conclude that some of form of iterative, test-driven software development was being practiced on real projects before 1971.
Years of my own digging has revealed that many other practices go back to the 1960's and earlier. It's believed, for example, that teams working on IBM's OS/360 in the early 1960's were doing integration builds.
The Report from the 1968 NATO Conference on Software Engineering describes some strikingly familiar problems faced by programmers then and programmers today, and includes pieces of advice that modern teams would do well to heed.
And then there's the technology itself. Advancements in programming languages had reached a plateau by the time I was born. By 1971 we had progressed from programming in binary with punchcards, via Assembler and 3GLs like FORTRAN, to typing early object oriented programs using languages like Simula and Smalltalk into text editors (maybe even using a Graphical User Interface, if you happened to work at Xerox Parc). LISP was invented in the 1950's, and folk who think functional programming is all trendy and new might like to reflect that on the fact that FP was old news by the year of my birth. The programming tools of today, while far more powerful in their scope, are essentially little changed from the programming tools of 1971. They typed programs into a text editor, and used a compiler to read the source code and generate machine-executable code.
And last night it struck me; would it be possible to synthesize a recognisably "modern" approach to software development using only the principles and practices and types of tools that were available in 1971?
If I shopped around the various texts written before 1971, could I create the requirements, design, programming, testing, configuration management and other disciplines I might need to produce valuable software in 2013?
Would it be simply, as some suggest, that all I would need to do is find out what we were calling it then and map that onto things we do today? Would there be big gaps? Would there be no gaps? Would it cover all 11 essential disciplines in my Back To Basics paper
Just for jolly, I intend to try and create a methodology synthesized out of these vintage disciplines that might be fit for 21st century purposes.
Wish me well!
November 14, 2013
November 12, 2013
The Tail Wagging The Dog - Why You Should Start With ExamplesIf you're one of those enlightened teams that agrees executable acceptance tests with your customer before writing software to satisfy those tests, how do you do it? Or, rather more importantly, when do you do it?
I observe on most teams that acceptance tests - examples of how a feature will be used - are based on a user story.
e.g., "As a business traveler, I want to search for hotels in a given area that have certain amenities with rooms within my budget" might yield tests like "Bill is looking for a hotel with free wi-fi within 1 km of Earls Court that has rooms for under £120 a night" and "Joanne is seeking a hotel with a gym and a swimming pool in central Manchester for under £140 a night"
Here's the thing with that; countless times I've found software I'm using doesn't quite let me do what I need to do. There's usually some detail they've overlooked that makes the software less useful in the real world. For example, travel web sites that don't let me know how fast the wi-fi is in the rooms. That's actually important to me. I work a lot in hotels, and wi-fi is often a bugbear. If it's slow and sluggish, or if the signal doesn't quite extend to the desk they've thoughtfully bolted to the wall so I end up having to check my email in the shower because that's where I can get a signal, well, it's a consideration.
So, "How fast is your wi-fi?" is a question I'd like to ask - I suspect many of us would - but the web applications for finding hotels don't let me.
Let me give you an example that's closer to home; unit test frameworks. So, I'm delivering a TDD training course for Acme Plc (name changed to protect the innocent), and they're stucking using Microsoft's proprietary unit testing tool. And we get to that part of the workshop where I'm asking them to refactor duplication out of their tests, and I innocently say "try turning your tests into one parameterised test" and they all put their hands up and say "Our tool doesn't support parameterised tests!"
WTF, Microsoft? That comes up often when writing unit tests. It's a powerful way to reuse test code and remove duplication. It can be an efficient way of getting better test assurance. If the tool designers had actually watched teams doing unit testing for any length of time, they would surely have spotted this need eventually.
I'm sure you can think of many examples from your own experience of software that appears to have been designed by people who didn't understand the problem they were trying to solve.
Because of this, I strongly recommend that teams begin by harvesting examples from the real problem domain. So "Bill is looking for a hotel with free wi-fi within 1 km of Earls Court that has rooms for under £120 a night" would be a real example of a business traveler looking for a hotel. (Or, indeed, "Jason wants a hotel that has a minimum 2Mb wi-fi connection with a strong signal at the provided work desk in the room").
Examine these examples, recreate them, live them. Only when they've sunk in should we look to generalising them into formats like "As a business traveler..."
Any scientists will tell you of the dangers of seeking data to prove your theory. A true scientist seeks a theory to explain the data.
October 21, 2013
Software Craftsmanship 2013 Will Be The LastWith just four days to go, this would seem like a good time to reveal that this Friday's Software Craftsmanshiop 2013 will be the final SC conference.
With more and more events springing up to serve the SC community here in the UK, it's inevitable that we'll be spreading ourselves a bit thinner, making fundraising events like SC20xx less viable.
But we've had a good run. A very good run, in fact. We were the world's first SC conference, and over the last 5 years we've raised in excess of £40,000 to support important work being done at Bletchley Park and The National Museum of Computing.
I'm also very proud to have maintained the founding principle that sessions must feature live hands-on coding and bags of audience participation. This has brought us some of the most innovative, challenging, and above all fun sessions we've seen at dev conferences for many years. It has also had the added side-benfit of excluding the usual suspects among conference speakers who tend to be - as my mother might put it - "all mouth and no trousers". Although we've consistently struggled to find session leaders (it takes cohones to code in front of a room full of, well, us), the standard of the sessions has been consistently high. Conferences take on a whole different character when session leaders have to show as well as tell.
What's really made the difference, though, is the people who've come along. You've rolled up your sleeves, powered up your laptops and had a go. And the buzz at every SC conference has been a joy to experience. In previous years, the conference had sold out before we announced any of the sessions, and above all else, people came to share their passion for a day with likeminded craftsmen and craftswomen.
To you, I express my deepest gratitude. If you're joining us for the Last Hoorah on Friday, let's make sure we go out with a bang.
October 17, 2013
Intenstive Test-driven Development, London Dec 7thJust a quick note to mention that I'm running my Intensive TDD Workshop on Saturday December 7th in central London.
Tickets are still just £99, and you get a heck of a lot of bang for that buck.
October 14, 2013
101 Uses For Polymorphic Testing (Okay... Three)Quick thoughts about some useful applications of polymorphic testing that I've tried with some success over the years.
Polymorphic testing is very simply done by writing tests such that the object under test can be dynamically substituted (e.g., by overriding a factory or builder method that creates it), making it possible to - for example - test that a SettlementAccount obeys the rules of its Account base class. (see Abstract Test pattern)
Unit testing frameworks like JUnit and NUnit will allow us to do this by extending test fixtures. They detect for running all the tests in the base fixture as well as new tests added to the subclass fixture. Simple and neat (and very possibly unintended.)
But what would you use this approach for in the real world?
Here are 3 examples:
1. Testing that classes satisfy supertype contracts
If you've worked on software that allows 3rd party developers to write their own components or extensions (e.g., device drivers for an OS), you will likely have come up against instances where developers implement your required interfaces but not necessarily obeying the rules that implementing those interfaces demands. Whatever a device driver does, it must obey the rules of being a device driver. If it doesn't, the result can be instability in the OS.
Having worked on products like these, I've seen many instances when writing a suite of tests that any classes implementing our interfaces must pass has paid big dividends later.
2. Testing for backwards compatibility
It's the First Law of Software Development - Thou shalt not break shit that was working.
This includes other people's shit.
When we release software to be used by other software, we're publishing an API against which 3rd party developers will invest time and effort. Every time we change our API, we potentially cause the need for them to spend even more time and effort adapting their code to the change.
This makes backwards compatibility not so much a "nice to have" as a commercial imperative for any software company that wants other developers to build solutions using their products.
I don't know about you, but when something's commercially imperative, I like to take steps to ensure it's happening.
Polymorphic testing, with some extra jiggery pokery, can be used to test that new versions of our public interfaces pass the tests we wrote against older versions.
We're talking specifically here about tests that bind only to our API. Change the internal design as much as you like. It's therefore useful to keep API-level tests separate from ordinary unit tests.
This can get a little technology-specific in practice. Running NUnit tests compiled against older versions of .NET assemblies is not entirely straightforward, for example. You have to go round the houses a bit to get around .NET's component versioning system. It's much more straghtforward in dynamic scripted languages like Ruby, where interfaces are discovered at runtime. But in most OO languages in use today, it's do-able, and economically worth doing in these situations.
Of course, if we've been observing the Open-Closed Principle like good little boys and girls, it's much easier.
3. Dynamically controlling the scope of test runs
This last technique I've found especially powerful. The ability to run the same functional test, say, with a real database and with data access mocked out can be very useful.
By overriding the methods that create objects under test, we can wire its collaborators together in all manner of permutations, and our test code is none the wiser.
This is especially helpful for teams who have hundreds of slow-running end-to-end system or acceptance tests. These tests are often where the logic of their code is being verified, having relied entirely on testing at that higher level, or on mostly interaction testing at the unit level. Although lack of unit tests will hurt you in other ways, at least with polymorphic testing it's possible to run those system-level tests entirely in-memory so they run fast.
So you might have a suite of acceptance tests implemented as JUnit fixtures that connect to a database etc, and then a suite that extends those fixtures to substitute objects that are wired to a mock persistence layer.
September 21, 2013
Duplicated Test Code & High CouplingJust a quick post about the relationship between duplication in unit test code and high coupling between unit tests and implementation classes.
Let's consider the example of FizzBuzz:
Here, I've "forgotten" to refactor the test code to remove some obvious duplication. And in doing so, I've duplicated the knowledge of how to create an instance of FizzBuzz and get it to fizzBuzz integers for me in multiple places.
This means that if I change the way we use the FizzBuzzer, multiple tests will break.
If I refactor the duplication away (and we'll touch on different strategies for that later), then I can isolate that knowledge so that it only needs to change in one place. E.g.
No doubt, some of you will rightly be thinking "I'd use a parameterised test", but the effect is exactly the same; less duplication means less duplicated coupling.
On a side note, how many parameterised tests would you use in this example?
I'd use three. Answers as to why on a postcard, please.
As an addendum, a short discussion about granularity and "level" of tests:
Some people say that they "don't test classes" but instead "test behaviour". In languages like Java it's not possible to test behaviour without knowing where that behaviour's coming from. So you end up writing code to access said behaviours, whether you like it or not.
Other folk say "I only write acceptance tests" and everything's tested either at the system boundary or just below it. The consequences of doing this are apparent in thousands of projects. When tests fail, you have to go through the call stack and figure out what went wrong and where.
There's actually no way of completely decoupling tests from the things they are testing. All programming languages have mechanisms for packaging code into discrete units (e.g., methods and classes), and to access those features we must know where they are. There's no magic we can apply that makes tests completely implementation-independent.
So I still strongly recommend the rule of thumb of unit tests only having one reason to fail - it's the reason I haven't needed to use a debugger in years - because it has implications for the "composability" of your software (it's very closely related to the Single Responsibility Principle). That, plus a well-developed refactoring muscle, tends to lead to tests that are very useful and loosely coupled to the implementation design.
Kevlin Henney made a good point on That Twitter about the number of parameterised unit tests he would use in this example.
I've done FizzBuzz so many times, I tend now to not treat numbers that are divisible by 3 and 5 as a special case. I'm possibly being swayed by my own implementation:
But to sum this up in 3 parameterised tests - implying 3 variants of behaviour with no order of precedence - would be potentially misleading, and therefore for completeness I might now plump for an extra parameterised test for numbers that are divisible by 3 and 5 to explicitly illustrate that the Fizz comes before the Buzz in those cases.
Explicitly, I would have the following parameterised tests, then:
1. Integers that are divisible by 3 should be substituted with "Fizz"
2. Integers divisible by 5 with "Buzz"
3. Integers divisible by 3 and 5 with "FizzBuzz"
4. Otherwise, just return the integer as a string
(My original 3 were: divisible by 3 starts with "Fizz", divisible by 5 ends with "Buzz", otherwise integer as string.)
September 18, 2013
My TDOOD Session at Software Craftsmanship 2013Just a quick hoorah to say that my session on Test-driven Object Oriented Design (TDOOD) has been accepted for SC2013.
So, if you're coming - and please do, it's in a very good cause - I hope you'll join me for fun and larks on October 25th.
All the details can be found at http://softwarecraftmanship2013.eventbrite.co.uk/
August 23, 2013
Software Ideas & Their Tendency Towards UbiquityOne marked way in which ideas in software development sometimes behave like religious movements is their tendency towards ubiquity.
It all starts innocently enough, with some bright spark saying "hey, you know what's worked for me?" Usually, it's a point solution to a specific problem, like writing the test before we write the code, or scheduling work so that developers pick up the next most important task from the queue as soon as they've completed the last one.
Simple ideas to solve particular problems.
Religions too, can start out with a simple idea like "hey, let's all treat each other the way we'd wish others to treat us" and so on.
But before we know it, the thought leaders of these religious movements are asking questions like "What does God have to say about wearing Nike on a Thursday?" and "What sort of toppings are acceptable on a low-sodium bagel?" and their religion starts to burrow its way into every aspect of our daily lives, dictating everything from beard length to when we can and cannot eat certain kinds of dairy products. Not unsurprisingly, the original underlying idea can get lost, and we end up with religious zealots who will gleefully nail you to a tree for wearing the wrong kind of underpants during a month with a 'Y' in the name, but who seem to have no hang-ups about nailing people to trees in the first place.
So, too, do some ideas burrow their way into other aspects of the way we write software. There seems to be a built-in predaliction for some ideas - usually methodological, but I've seen it happen with tools, too - to grow to become all-encompassing, and for the original underlying idea to get forgotten.
And I can understand the motivations behind this; particularly for consultants. A hammer gets a much larger potential market if we claim it can tell the time, too. We can dramatically extend the scope of our influence by making what we're experts in apply to just about everything.
August 22, 2013
Published Interfaces & Common CourtesyThe mainstream media often depicts software development as a lonely activity, with us lowly "geeks" (that's what they like to call us) staying up 'till all hours - probably in our mum's basement where we still live - poring over the details of the Visual Basic GUI we're rigging up to track the killer's IP address.
In reality, software development is far from a game for one and no more players. Software doesn't happen in a vacuum. For a start, there is other software, and therefore other software developers.
When I started my career as a programmer, this fact loomed large on people's radars. It was the early years of component-based development - an evolution of the kind of architectures that were popularised by UNIX, where our software glued together third party components that already did stuff we found useful in our own code.
Reusing other people's software creates dependencies on that code and therefore on the coders who coded it. Sometimes we were writing the component that got reused, sometimes we were reusing someone else's component.
Rules were well-established by the time I started doing this for a living on how everyone could get along and play nicely in this complex heterogeneous web of dependencies.
The first rule was that reusable components should work. We should be able to trust them, to depend on them, to do what it says on the tin. This concept was best formalised in Bertrand Meyer's discipline of "Trusted Components". It's a dangerous world indeed when all our software depends on things that are not dependable.
Assuming the components we shipped were trustworthy and dependable enough, once we'd shipped them, lots of other programmers might start writing code that reused them. We learned long before I was born of the consequences of this. If, for any reason, we changed the contracts for using the services of one of our components - e.g., we added a new parameter, or tightened a pre-condition - then that potentially broke a lot of client code that other people had written against our API. It may have been a small change for us, but it could stack up to a huge change effort - with an associated huge cost - for everyone depending on us.
So we agreed not to do that unless we really, really - cross our hearts and hoped to die - absolutely had no other choice. In practice, it meant that the benefits of changing an API had to outweigh the potential costs incurred. The more code out there depended on us, the bigger the pay-off had to be. And, as a common courtesy, there had to be sufficient benefit - e.g., in enhanced functionality or better performance - for the client code developers and their end users.
So, on top of that basic principle of Trusted Components, we added the principle of published interfaces.
A programming interface, once made available for other developers to use - through a release process, presumably - could only evolve in such a way that code written against previous versions of that API would not be broken by those changes.
We may well be sitting alone, late at night, in mum's basement, surrounded by pizza boxes and empty Mountain Dew cans, writing VB apps to track murder suspects. But it's very likely that our app is built on top of software that's already been written, requesting the services of said 3rd paty code through published interfaces. That is a means by which we are now interacting with other software developers.
And if we were to offer our VB-killer-IP-address-tracking software as a library for other developers to reuse in their mum's basements, that is how they will interact with us.
I threw in the phrase "common courtesy" earlier quite deliberately, because that's what's needed in any dialogue - even one taking place through published APIs. By keeping our APIs backwards-compatible with previous releases, we show client code developers the common courtesy of not breaking their shit every time we do a release.
I only bring this up because I've been getting the sense that this courtesy has been somehow lost in recent years. APIs are more important than ever, with some pretty huge businesses publishing and consuming 3rd party code over Teh Internets, and the potential costs of changing these APIs - including the rules for using them - can run into billions of dollars taken as a whole. And yet, some pretty big-name players appear to have no qualms about breaking client code to serve their own - as it turns out, probably nefarious - purposes.
I had a 6am Skype pairing session with a client in India this morning who has been dealing with precisely this problem. Due to API changes by a web company whose services his business relies on, he's suddenly found that a big chunk of their code will now have to be re-written. And this is all so they can get to a point where their software did what it did before the supplier broke it. There is no benefit to his business, just cost.
As far as I'm concerned, that is the height of rudeness.
So, my two tips for working with 3rd party libraries and services:
1. If you're writing the client code that consumes these services, make sure the core logic of your software is protected from changes to those APIs. Wrap up the mechanics of interacting with the APIs behind interfaces that you own, and leave the tiniest sliver of integration code that does the actual interacting in one place. This will also have the benefit of making those external libraries and services swappable and mockable.
2. If you're writing components or services that are to be reused, and you want to know if the latest version will potentially break existing client code, then run the old tests* against the new code. (Since test code is client code, too.) If any tests fail, that means a change you've made may break existing client code, and your new version isn't backwards compatible.
3. (And this is very important) If you absolutely must change the API in a way that's not backwards-compatible, involve the client code developers in that decision. You're about to make a costly decision on their behalf. What is the motivation for doing it? What are the benefits to them? Do they think it will be worth it? Don't just spring it on them (if you'll excuse the pun.) Engage them in a dialogue
* Tests that only use the published interfaces, obviously