Codemanship Exercise “Play-through” – CD Warehouse Part #3

In yesterday’s post, I continued with the CD warehouse exercise from the Codemanship TDD course, adding a feature to search the catalogue by artist and title.

In this post, I’ll be working on code to receive batches of CDs into the warehouse from record labels.

There are three examples for this use case in my test list:

  • Receive batch of CDs
    • Of existing title in catalogue
    • Of new title (add to catalogue)
    • Of multiple titles

In the first example, we’re receiving copies of a CD that’s already in our catalogue, so we would expect to add those copies to the existing stock. Let’s write a failing test for that.

To pass this test, we just need to search the catalogue for a matching CD and then add the copies to its stock.

Now our test is passing, it’s time for a mini code review. Catalogue now breaks our principle that classes should only do one job. I can easily imagine wanting to change how we receive CDs independently of how we search the catalogue, so this class needs to be broken up.

I’ve extracted a containing class that uses the catalogue, and updated the test to change the target of the receive() method call.

There’s another thing that’s bugging me about the receive() method – it accesses CD’s data directly to add copies to the stock. Although that isn’t Feature Envy, strictly speaking, it most certainly isn’t encapsulation. Let’s fix that.

With that fixed, we can move on to the next test – receiving copies of a CD that isn’t in the catalogue.

To pass this test, we need to check if the CD’s not in the catalogue (i.e., search() returns null), create a new CD if it isn’t and add it to the catalogue before adding the received copies to its stock.

We’re on a green light again, which means it’s time for another mini code review. The first thing that jumps out at me is the distinctly unencapsulated way Warehouse adds the new CD to the catalogue. Let’s fix that.

The next thing that catches my eye is the hardwired dependencies on CD and Description. Now, objects have to be created somewhere, so some code has to use new. But if our goal is to have objects use each other polymorphically, then I have a simple rule: objects that create instances of other classes shouldn’t use them, and objects that use instance of other classes shouldn’t create them. You can create an instance or use an instance, but not both.

It’s also good practice to maintain object creation in one place (D.R.Y.), and by itself so changing – say – the constructor of CD doesn’t impact any other logic.

In fact, we already have a candidate for where that might be: the add() method I extracted and moved to Catalogue. We could move object creation to there and return the new CD to Warehouse.

This simplifies Warehouse and removes its direct dependencies on the implementations of CD and Description. But it just pushed our problem down the call stack to Catalogue, which now has those direct dependencies.

This breaks my swappability rule: Catalogue creates an instance of CD, and it uses CD (by calling cd.matches()). Let’s extract the creation into its own factory method.

And then let’s move that factory method into its own class that we can inject into Catalogue so it doesn’t need to have those hardwired implementation dependencies.

(And, yes, I’m not happy with a class name ending in “Factory”, either. Suggestions for a better name on a postcard, please.)

Some of you may be thinking “Doesn’t Catalogue have two jobs now?” Arguably, yes: searching the contents for CDs and adding CDs to the contents. But both jobs revolve around the same data. If we split these responsibilities apart, we’d have to unencapsulate the contents. Eliminating one code smell will introduce another. It’s very much in the nature of software that we’ll have to make design compromises – we can’t tick all of the boxes all of the time. I’m going to leave this as it is fr now, and we’ll see how the design evolves.

One thing that has occurred to me at this point is: what happened to the CD’s price – where is that set? There’s an extra test here we need to add.

To pass this test, we add price to our factory method.

And pass that data through from Warehouse.

Back on a green light, time for a mini code review. Not much has changed in our source code. Two things strike me; first, we have a Data Clump emerging, with the parameters title, artist and price now appearing in several method signatures. Also, three parameters is right on the edge of what I’d consider to be a Long Parameter list. Making that last test pass illustrated why this can be a problem; I found myself adding the same parameter multiple time to multiple methods. What happens the next time we have to add another piece of data to that Data Clump? Let’s simplify these signatures by preserving the whole object.

While we’re in refactoring mode, let’s simplify the test.

(Yes, I jumped the gun on the Rule of Three – but it was such obvious duplication, with no additional readability, that it was bugging me.)

We have one more test on our list for this feature. How does the warehouse receive batches of multiple CDs?

To pass this test, we can simply repeat the logic of receiving a single CD in a forEach loop.

Time for one more mini code review before we take a break. It’s important to remember that on any non-trivial design, a perfect score is rarely possible. I’ve made compromises, based on my own judgement and 37 years of programming hard knocks, that I’m happy to live with. Importantly, I thought about them, and I know why I made those compromises (e.g., introducing Feature Envy because I wanted to split up a cohesive class that was doing two distinct jobs – like Catalogue and Warehouse, or the reverse decision to keep searching and adding in Catalogue to avoid un-encapsulating its contents).

As the code currently stands, I’m good to go. No doubt if I leave the design in to soak overnight, it may taste different in the morning.

Let’s cross off the test cases we passed today from our list, and take the night off.

  • Buy a CD
    • Payment accepted
    • Payment rejected
    • Out of stock
  • Search for a CD (by title and artist)
    • Warehouse has matching title
    • No match found
  • Receive batch of CDs
    • Of existing title in catalogue
    • Of new title (add to catalogue)
    • Of multiple titles
  • Review a CD
    • Just a rating
    • Rating and review

Coming next: Reviewing a CD, and a final code review.

Codemanship Exercise “Play-through” – CD Warehouse Part #2

In my previous post, I started tackling the CD warehouse exercise from the Codemanship TDD course in JavaScript.

The focus of the exercise is on applying basic software design principles while test-driving a design. I encourage developers on the course to stop at every passing test – every “green light” – and do a mini code review where they ask 7 questions:

  • Is the code easy to understand?
  • Is there duplication (I encourage them to apply the Rule of Three for this exercise)?
  • Is there unnecessary complexity?
  • Do methods/functions and classes/modules do more than one job?
  • Are classes/modules encapsulated?
  • Are dependencies between classes/modules easily swappable?
  • Do classes/modules depend on things they don’t use?

At each green light, we inspect the code we’ve added or changed for each of the principles, and if the code is found wanting in any of these aspects, we refactor before moving on.

I started my take on the exercise by identifying use cases in the problem description (looking for user actions like “Buy a CD” and “Review a CD”), then making a list of test cases for each use case.

I began working through this list one test case at a time, crossing off each one once I’ve:

a. Passed the test, and

b. Refactored if necessary

I often find making a list of tests before I start coding very helpful. It forces me to analyse the problem a bit, and also gives me a rough roadmap for tackling a problem in a test-driven way. (This is the Test List pattern described in Kent Beck’s book Test-Driven Development by Example.)

This is where we got to yesterday.

  • Buy a CD
    • Payment accepted
    • Payment rejected
    • Out of stock
  • Search for a CD (by title and artist)
    • Warehouse has matching title
    • No match found
  • Receive batch of CDs
    • Of existing title in catalogue
    • Of new title (add to catalogue)
    • Of multiple titles
  • Review a CD
    • Just a rating
    • Rating and review

The next test on our list is searching for a CD by artist and title where the warehouse contains a matching CD. Let’s write a failing test.

I’ve set up my catalogue to contain just one CD – the one we’re searching for – which is the simplest test case I could think of.

In yesterday’s tests, I found the implementations to be quite trivial, so I didn’t triangulate them through a series of examples, I just wrote the Obvious Implementation (another of the patterns described in Kent Beck’s book). My gut is telling me that for this behaviour, there’s a bit more to it, so I’ll pass this test by doing something very simple.

Now the test is passing, it’s time to do a mini code review. I go through my checklist and it looks to me like we’re good to move on to the next example. Remember that one of the goals of TDD is to get feedback as frequently as possible, making fewer design decisions in each feedback cycle. If you feel that you’re making too many design decisions before seeing the test pass, maybe take smaller steps. This is a skill that requires a lot of practice. Even after two decades of doing TDD, I still regularly find myself backing out of a test that’s taking too long (and frequent commits are essential for that ability) and trying to break the problem down a bit more.

To pass both of these tests, I’ll need to do something a bit more sophisticated in the implementation.

This is fine for now, and both tests pass. Time for another mini code review.

There’s obvious duplication in the tests, but as we’re strictly applying the Rule of Three, we won’t do anything about that just now. But what does jump out at me is the Feature Envy that the search() method has for the fields of CD. Let’s extract that code into its own method.

And then let’s move that new method to where it clearly belongs.

One point that I think a lot of developers miss is that refactorings can have side effects – that is, eliminating one code smell (like Feature Envy) can introduce new code smells. Moving the matches() method – which has nothing to do with buying a CD – to the CD class now means that CD has more than one distinct responsibility.

I can imagine wanting to change or vary how we match CDs (e.g., partial instead of exact matches) independently of how we buy them. Let’s split this class up to separate the responsibilities.

Now we have to look at the nature of the dependency between CD and Description. It’s not swappable by dependency injection, so CD depends on that specific implementation – it’s hardwired. Let’s introduce it as a constructor parameter.

Okay. All the tests are passing and my code so far is clean, so I can move on to the next failing test, which is for when there’s no matching CD in the catalogue.

When I run this test, it surprisingly passes. I would have expected an array index out of bounds error, coming as I am from stricter languages, but JavaScript seems just fine referencing the first item in an empty list. The result is null. A discussion about the merits or otherwise of this language feature is for another day. For practical purposes, it seems to work, and this test – although not failing – is still a valid and useful test – what does it do when no matching CDs are found? So I’ll keep it as part of my suite.

That crosses off two more test cases from my list, and we’ll park it there for today.

  • Buy a CD
    • Payment accepted
    • Payment rejected
    • Out of stock
  • Search for a CD (by title and artist)
    • Warehouse has matching title
    • No match found
  • Receive batch of CDs
    • Of existing title in catalogue
    • Of new title (add to catalogue)
    • Of multiple titles
  • Review a CD
    • Just a rating
    • Rating and review

One final thought; folk often observe when they watch me doing TDD that it can sometimes feel like more refactoring than writing and passing tests. This is normal and healthy, just as long as we’re making good progress. I’ve found that developers usually underestimate how much refactoring’s required to keep code clean. Don’t skimp!

Coming next, receiving batches of CDs…

 

 

 

 

Codemanship Exercise “Play-through” – CD Warehouse Part #1

It’s an extremely hot day in London, and I’m staying indoors until the temperature falls below 30 degrees C. Too hot for chores or putting up more of my flatpack backlog, I thought it might be fun to do a “play-through” blog post about one of the practical exercises from the Codemanship TDD workshop.

The exercises comes just after we discuss basic software design principles on the first day of the course. At this point, we’ve covered Red-Green-refactor, and dwelled on the refactoring discipline in previous exercises. Now we’re scaling things up a little from FizzBuzz-scale problems to a more business-like set of requirements.

From the book (page 101):

Test-drive some code that manages the stock and orders of a CD
warehouse. Customers can buy CDs, searching on the title and the
artist. Record labels send batches of CDs to the warehouse. Keep a
stock count of how many copies of each title are in the warehouse.
Customers can only order titles that are in stock. Use dependency
injection to fake credit card payment processing, so we can get on
with our CD warehouse design without worrying about how that
will be done.
Customers can leave reviews for CDs they’ve bought through the
warehouse, which gives each title an integer rating from 1- 10 and
the text of their review if they want to say more.

I almost always start by listing use cases, and enumerating key scenarios for each one. (This is essentially the Test List pattern from Kent Beck’s Test-Driven Development by Example book.) Buried in this text is a handful of use cases we need to satisfy.

  • Buy a CD
    • Payment accepted
    • Payment rejected
    • Out of stock
  • Search for a CD (by title and artist)
    • Warehouse has matching title
    • No match found
  • Receive batch of CDs
    • Of existing title in catalogue
    • Of new title (add to catalogue)
    • Of multiple titles
  • Review a CD
    • Just a rating
    • Rating and review

In this exercise, we’re going to work are way through this list of test cases, doing the simplest thing possible to pass each test, and flesh out a design that not only passes all of these tests, but also satisfies some basic design principles:

  1. The code must be easy to understand
  2. The code must be low in duplication (in this exercise, we’ll apply the Rule of Three – if we see three examples of duplicated code, we’ll refactor it to make it D.R.Y.)
  3. The code must be simple (long methods, deep-nested if’s and loops, etc, will be refactored to make them simpler)
  4. The modules should do one job only
  5. The modules should Tell, Don’t Ask (I instruct students to look for a code smell called Feature Envy)
  6. Module dependencies should be easily swappable by dependency injection
  7. Modules should only depend on things they actually use (in Java, this means presenting client-specific interfaces, in JavaScript it means not importing things a module isn’t using.)

The exercise is a TDD exercise, so we’ll start by writing a failing test, quickly get that test passing in the simplest way we can think of, and then – and this is the point of this exercise – stop and do a little code review.

Yes, you read that right. Not on a pull request. Not at the end of a sprint. A mini code review on every green light.

We’ll pause, go through all the code we’ve added or changed, and apply our checklist. Any code that doesn’t tick all the boxes should be refactored until it does.

I’m going to tackle this exercise in JavaScript, running on Node.js, because I could do with the practice.

Let’s start with “Buy a CD – payment accepted”. First, I’ll write an empty Mocha test for that scenario.

I’ve interpreted the requirement to mean that when you successfully buy a copy of a CD, the outcome is that the stock count of that CD is reduced by one. (Many pairs interpret managing stock count as a use case, but it really isn’t. A use case is triggered by a user action, like buying a CD. The stock changes as an outcome.)

Let’s fill in the blanks, starting with the assertion:

And then let’s work backwards to the set-up via the action:

Remember that the payment being accepted is part of the set-up for this scenario, so – as per the instructions – we do need to provide that information somehow. Since, in real operation, that data will come from an external source (the payments processor), we use a stub to provide it in the test.

This is our first failing test. Passing it is easy.

This brings us to our first green light, which means it’s time to do a mini code review.

  • Is my code easy to understand so far? Well, I think so. But what do you think?
  • Is there any duplication we need to worry about yet? Not that I can see.
  • Is the code simple? It’s early days, but so far, yes.
  • Does each part do one job? See above. For this single test, there’s only one job to do.
  • Can I see any Feature Envy? Nope.
  • Are the dependencies swappable? There’s only one so far – in the class CD in the payments object – and that’s being passed in from outside. So, tick.
  • Does CD depend on the implementation of StubPayments? Nope. But, right now, I have got all my code in a single file with the test code. That needs fixing, so I’ll split them into their own files, and move the source code into the root folder away from the tests.

Let’s move on to the next test on our list, “Buy a CD – payment rejected”.

This is our next red light. To pass this, we now need to have the CD ask the payment processor to give us a response, as defined by the stub in our test.

The stub returns whatever response the test tells it to. Stubs should never get any smarter than that.

So, we’re on our second green light. Time for a mini code review. One thing that jumps out at me is this line here in CD’s buy() method:

payments.process doesn’t read as a question. We could make the IF statement clearer by introduced a variable:

The rest of the code ticks the other boxes, but we can see there’s some duplication emerging in our test code.

In this exercise, we’re applying the Rule of Three – if we see three examples of duplication, we refactor. So let’s hang fire on this for now.

Time for the next test.

Passing this test is easy.

Another green light means it’s time for another mini code review. The buy() method of CD has two branches, which is right on the edge of my personal tolerance for complexity. I think I can live it with it, but another IF statement and I’d take action.

Now, about that duplicated test set-up code: we have three examples, which means it’s time to do something about it.

First, let’s tackle the most obvious duplication.

We must take great care when removing duplicated set-up code from tests that we don’t end up with a situation where developers have to keep referring to code outside of each test to understand the scenario. In this case, the credit card details aren’t important. (If we added test cases for bogus cards etc, then it would be different.) So I think we can safely factor our the credit card declaration without impacting readability.

With the rest, it’s not so straightforward. We need to know what the initial stock of the CD is. We need to know what response the payments processor will give. If we wrote more and more tests with this set-up, I would argue for factoring out shared set-up code to limit how much of our test code is bound to the implementation. But, as this is the third and final of these tests, I don’t think it’s worth it. So I’m living the test code as it is and moving on to the next use case.

Coming in Part #2 – Searching for CDs…

 

Communicating Through Code – Part 2

A wee while ago, I threw out an idea for a coding exercise designed to challenge our skills at writing code that tells a story.

In the basic version, you’re asked to design an API that would enable you to tell the story of Humpty Dumpty, using modules/classes, functions/methods, fields, variables, parameters etc of maximum one word’s length. (It also served as a useful exercise in working backwards in your editor.)

All fine holiday fun. But my Humpty Dumpty code didn’t actually do anything.

The second part of the challenge is to repeat this exercise with code that actually does real work.

I’m using my faithful video library example. When members donate videos to the library, a bunch of stuff needs to happen.

I wrote it down in plain English first:

  • Add donated video to catalogue

  • Register one rental copy of the donated video

  • Award the donor 10 priority points

  • Alert members about the donated video

Then I repeated the process I applied to Humpty Dumpty, except this time, the code actually does what it says it does. This time, I set myself a limit of 2 words per identifier, and allowed myself to ignore “noise” words like “a” and “the”.

function VideoLibrary(catalogue, members, {register, of}, award, alert) {

    return (donatedVideo, donor) => {
        // add donated video to catalogue
        // register one rental copy of the donated video
        // award the donor 10 priority points
        // alert members about the donated video

        add(donatedVideo).to(catalogue);
        register(1).rentalCopy(of(donatedVideo));
        award(donor, 10).priorityPoints();
        alert(members).about(donatedVideo);
    };
}

Then I repeated the exercise, only this time I started out by writing the raw implementation code that does the work:

function VideoLibrary(catalogue, members) {

    return (donatedVideo, donor) => {
        // add donated video to catalogue
        // register one rental copy of the donated video
        // award the donor 10 priority points
        // alert members about the donated video

        catalogue.push(donatedVideo);
        donatedVideo.copies += 1;
        donor.priorityPoints += 10;

        const nodemailer = require('nodemailer');

        const recipients = members.map((member) => { return member.email; }).join(',');

        const email = {
            from: 'alerts@myvideolibrary.com',
            to: recipients,
            subject: 'A new movie has been added to the library - ' + donatedVideo.title,
            text: 'Dear Member, Just to let you know that '
                + donatedVideo.title +
                '(' + donatedVideo.year +
                ') has been added to the library and is now available to borrow'
        };

        var transporter = nodemailer.createTransport({
            service: 'gmail',
            auth: {  // use your gmail user name & password. You may need to set your account to allow less secure apps
                user: '###youremail###@gmail.com',
                pass: '###yourpassword###'
            }
        });

        transporter.sendMail(email, function(error, info){
            if (error) {
                console.log(error);
            } else {
                console.log('Email sent: ' + info.response);
            }
        });

    };
}

And refactored to make it read like the description. This I found especially challenging, but was delighted at how the end result turned out simpler and cleaner – making effective use of closures to encapsulate details. (Check out the full refactored code at https://github.com/jasongorman/text-to-code-refactoring .)

Now, I’ll remind you that this is just an exercise, and for sure the finished design isn’t without its problems. Here’s we’re specifically ticking two design boxes: code that works, and code that clearly communicates its intent.

As I take more passes at this, the exercise gets more challenging. For example, can I make the dependencies swappable? Managed to do that, but VideoLibrary now has many parameters. I managed to preserve the readability of the code in the donate() function, though. So that’s another tick.

Moving down the call stack, I repeated this process for the code that sends email alerts to members about donated videos. So it can be done at multiple levels of abstraction.

function alert(members) {
    return {
        about: (donatedVideo) => {
            send(newAlert(toAll(members)).
                about(donatedVideo), by(nodemailer));
        }
    }
}

One final little challenge I set myself this morning was to make all the data immutable. This proved tougher than I expected, and I couldn’t find a way to do it that preserved the readability of the code that tells the story.

function VideoLibrary(catalogue, members, {register, of}, award, alert) {

    return (donatedVideo, donor) => {
        // add donated video to catalogue
        // register one rental copy of the donated video
        // award the donor 10 priority points
        // alert members about the donated video

        const rentableVideo = register(1).rentalCopy(of(donatedVideo));
        const updatedCatalogue = add(rentableVideo).to(catalogue);
        const rewardedDonor = award(donor, 10).priorityPoints();
        alert(members).about(rentableVideo);

        return {
            video: rentableVideo,
            donor: rewardedDonor,
            catalogue: updatedCatalogue,
            members: [...members.filter((m) => m.email !== donor.email), rewardedDonor]
        };
    };
}

Disappointing. But it served as a neat illustration of the potential cost of immutability in increased complexity and reduced readability. There’s no free lunches!

I’ll give it some thought and try that refactoring again. Maybe there’s a way to have our cake and eat it?

Anyhoo, I recommend trying this exercise with your own code. Write down in plain English (or whatever language you speak) what the code does, then see if you can translate it into fluent code that clearly tells that story. As your design emerges, see if you can apply that process on multiple layers. And see if you can produce a design that both tells the story and ticks other design boxes at the same time.

Constraints Can Be A Good Thing

Sometimes we get it too easy.

I often think of the experiences I’ve had recording music using software plug-ins instead of real hardware.

Time was that recording music was hard. My home recording set-up was a Tascam 8-track tape recorder, three guitars, a choice of two amplifiers, a bunch of guitar pedals, a microphone for recording guitars (you can’t just plug your amp directly into a tape recorder, you have to mic it) and occasional bad vocals, a bass guitar, a drum machine and a Roland synth module controlled by MIDI from my 486 PC.

Recording to tape takes patience. And, while 8 tracks might sound a lot, it’s actually very limiting. One track is vocals. For rock and metal, you record rhythm guitars twice for left and right to get a stereo spread. Bass guitar makes four tracks. Lead guitar makes five and six, if you want harmonies. Drums take tracks seven and eight for stereo. Then you “bounce” the guitars down to two tracks – basically output a stereo guitar mix and record L and R on just two tracks – to make space for stereo keyboards at the end.

Typically I needed several takes for each guitar, bass and vocal part. And between each take, I had to rewind the tape to exact point where I needed to “punch in” again. If, during mixing, I decide I didn’t like the guitar sound, I had to record it all over again.

Fast forward to the 2010s, and I’ve been doing it all using software. The guitars are real, but the amps are digital – often recorded using plug-ins that simulate guitar amps, speaker cabinets and microphones – and I can choose from hundreds of amp, cabinet and microphone models. If I don’t lie the guitar sound, I can just change it. No need to re-record that guitar part.

Th3-Main

And I can choose from dozens of software synthesizers, offering thousands upon thousands of potential sounds. I can have as many virtual Minimoogs or Roland Jupiter 8s as I like. My i7 laptop can run 10-20 of them simultaneously.

mini-v-image

My drums are now created using a powerful multi-sampled virtual instrument that allows me to choose from dozens of different sampled kits, or create my own custom kits, and I can tweak the recording parameters and apply almost limitless effects like compression and EQ to my heart’s content. And, again, if I don’t like the drum sound, I don’t need to wind back a tape and record them all over again.

sd2

My Digital Audio Workstation lets me record as many tracks – mono and stereo – as my computer can handle (typically, more than 30 in a final mix), and I can route and re-route the audio as many times as I like.

Because I use software plug-ins for effects like echo/delay, reverb, chorus, EQ, compression and more, I have almost limitless production options for every track.

And, most mind-bending of all, I can record take after take after with no rewinding and the audio quality never degrades.

Digital is aces!

Except… About a year ago I invested in my first hardware synthesizer for a very long time. It’s a Korg Minilogue – a proper analog synthesizer (the first analog synth I’ve owned – and to operate it you have to press buttons and twiddle knobs). Unlike my Roland digital synth, it’s not “multi-timbral”. It makes one sound at a time, and can play up to 4 notes at a time. My Roland JV-2080 could make 16 different sounds a time, and play up to 64 notes simultaneously.

minilogue

Compared to the software – and the digital synth hardware – the Minilogue is very limiting. It’s significantly harder recording music with the Minilogue than with a software synthesizer.

But when I listen to the first quick demo track I created using the real analog synth and tracks I created using software synths, I can’t help noticing that they have a very different quality.

Those constraints led me to something new – something simpler, into which I had to put more thought about the design of each sound, about the structure of the music, about the real hardware guitar effects I used on each “patch”.

I’m not necessarily saying the end results are better than using software synths. I’m saying that the constraints of the Korg Minilogue led me down a different path. It changed the music.

I’ve been inspired to experiment more with hardware instruments, maybe even revisit recording guitars through mic’d up tube amps again and see how that changes the music, too. (And this is a great time to do that, as low-priced analog synths are making a comeback right now.)

All this got me to thinking about the tools I use to create software. It sometimes feels like we’ve maybe got a bit too much choice, made things a little too easy.  And all that choice and ease had led us to more complex products: multiple tech stacks, written in multiple languages, with lots of external dependencies because our package managers make that so easy now. etc etc.

Would going back to simple, limited editors, homogenous platforms, limited hardware and so on change the software we tend to create?

I think it may be worth exploring.

 

Code Craft is Seat Belts for Programmers

Every so often we all get a good laugh when some unfortunate new hire or intern at a major tech company accidentally “deletes Google” on their first day. It’s easy to snigger (because, of course, none of us has ever messed up like that).

The fact is, though, that pointing and laughing when tech professionals make mistakes doesn’t stop mistakes getting made. It can also breed a toxic work culture, where people learn to avoid mistakes by not taking risks. Not taking risks is anathema to innovation, where – by definition – we’re trying stuff we’ve never done before. Want to stifle innovation where you work? Pointing and laughing is a great way to get there.

One of the things I like most about code craft is how it can promote a culture of safety to try new things and take risks.

A suite of good, fast-running unit tests, for example, makes it easier to spot our boos-boos sooner, so we can un-boo-boo them quickly and without attracting attention.

Continuous Integration offers a level of un-doability that makes it easier and safer to experiment, safe in the knowledge that if we mess it up, we can get back to the last version that worked with a simple hard reset.

The micro-cycles of refactoring mean we never stray far from the path of working code. Combine that with fast-running tests and frequent commits, and ambitious and improbable re-architecting of – say – legacy code becomes a sequence of mundane, undo-able and safe micro-rewrites.

And I can’t help feeling – when I see some poor sod getting Twitter Heat for screwing up a system in production – that it was the deficiency in their delivery pipeline that allowed it to happen that was really at fault. The organisation messed up.

Software development’s a learning process. Think about when young children – or people of any age – first learn to use a computer. The fear of “breaking it” often discourages them from trying new things, and this hampers their learning process. never underestimate just how much great innovation happens when someone says “I wonder what happens if I do this…” Remove that fear by fostering a culture of “what if…?” shielded by systems that forgive.

Code craft is seat belts for programmers.

Code Craft is More Throws Of The Dice

On the occasions founders ask me about the business case for code craft practices like unit testing, Continuous Integration and refactoring, we get to a crunch moment: will this guarantee success for my business?

Honestly? No. Nobody can guarantee that.

Unit testing can’t guarantee that. Test-Driven Development can’t guarantee that. Refactoring can’t guarantee it. Automated builds can’t guarantee it. Microservices can’t. The Cloud can’t. Event sourcing can’t. NoSQL can’t. Lean can’t. Scrum can’t. Kanban can’t. Agile can’t. Nothing can.

And that is the whole point of code craft. In the crap game of technology, every release of our product or system is almost certainly not a winning throw of the dice. You’re going to need to throw again. And again. And again. And again.

What code craft offers is more throws of the dice. It’s a very simple value proposition. Releasing working software sooner, more often and for longer improves your chances of hitting the jackpot. More so than any other discipline in software development.