The Obligatory Post About A Profession of Software Development

When I moan about the immature state of our now 80 year-old profession, some folks will nod along, some will argue that *all* professions have incompetent practitioners (medicine is often cited as an example), and some will say that there’s no widely-agreed-upon body of knowledge on which to build a mature profession.

I’m not sold on the notion that, say, the medical profession is just as bad. For sure, there are bad doctors. But it’s a question of degrees. Are 90% of them incompetent? I’d be surprised to meet a doctor who’d never heard of sterilising instruments.

But I meet the equivalent software developers all the time, who have somehow missed out on some pretty fundamental stuff. I consider continuous testing to be “foundational”, for example: the equivalent of sterilising our instruments. We really shouldn’t be operating on production code without it. And yet it remains stubbornly a minority pursuit, despite all the evidence that it tends to produce better outcomes for our patients 🙂

I’m also not sold on the argument that there’s no consensus on what works and what doesn’t in software development. When it comes to the foundational stuff, the jury is very much /not/ out. While you can always find people who will disagree that, e.g., iterative and incremental delivery or user-centred design are good things generally, the fact is that they’ve enjoyed a majority consensus for decades.

What we would recognise as modern software development has been established since the late 1980s. Not much has been added to that body of knowledge since (though there’s been plenty of “churnovation” – variations on those themes – and, frankly, our computers just got a *lot* faster, enabling us to turn the dials up way past 11).

It’s some of these core foundations that teams learn on my training courses (check out the Codemanship YouTube channel for oodles of free tutorials, BTW).

So I believe that we are somewhat exceptional in being a profession of perpetual beginners, for a variety of reasons, and I also believe that there is a consensus, backed by a significant body of data (e.g., DORA), on what – in general terms, at least – tends to produce the best outcomes for our customers.

And I can’t help feeling that our profession could organise itself better to ensure that fewer developers can work for years and years without being exposed to these fundamentals.

The A-Z of Code Craft – N is for Non-Blocking

Image

By bike, it takes about 15 minutes to get from my house to Wimbledon Village in South West London. In a sports car that’s 10 times as fast as a bicycle – let’s call it a “10x” mode of transport – it still takes about 15 minutes to get from my house to Wimbledon Village.

When we travel on London’s roads, the journey time’s mostly determined not by the performance of our vehicle, but by how much time we spend waiting. Waiting at traffic lights. Waiting at junctions. Waiting at pedestrian crossings. Waiting to join roundabouts. It’s mostly waiting.

During rush hour, the average journey speed in London is just 9 miles/hour, whether you’re in a Porsche 911 or on a bicycle. This is not a limit of your vehicle, this is a limit of the system your vehicle has to work within.

We see a similar effect with software developers. Take any “10x” developer and put them in a 1x system, and you’ll get 1x performance out of them every time. (Yes, even if they use an “A.I.” coding assistant!) Fitting a jet engine to your car isn’t going to get you to Wimbledon Village any sooner.

If you really want to get more value sooner out of a dev team, don’t focus on the performance of the developers, focus on reducing the time they spend waiting – the time they spend blocked from creating value by the system they’re working within.

Sadly, blocking behaviours are rife in our industry. Pull Request code reviews are a good example, where a developer’s changes sit on the shelf waiting to be approved before they can make it into the end product.

There are many other examples of blocking behaviour, such as waiting for customer input, waiting for the UX designer to provide wireframes, waiting for a QA team to test the software, and so on. The average team spends most of their time not moving forward, but sitting at proverbial traffic lights.

In concurrent programming, we have a concept of “non-blocking” processes that can continue without waiting for another process to finish. Maximising the non-blocking parts can hugely improve the performance of the system as a whole.

There are so many common blockers that it’s beyond the scope of this little essay to discuss them all, but I can offer some general advice on unblocking your development teams:

  • Trust and empower teams to make more of the decisions
  • Encapsulate the knowledge and skills needed to deliver user/business outcomes within teams
  • Limit the amount of work in progress. It can be hard to see the bottlenecks and blockers when the team has a bunch of half-finished features in play.
  • View all handovers and sign-offs with hostile suspicion. They are the traffic lights of your dev process.
  • If something’s hurting (e.g., merging feature branches), do it more often. If possible, do it continuously. This is especially true about communication.
  • Java Jane doesn’t have to wait for the DBA to add a column if she can do it herself. T-shaped developers get blocked far less often.
  • More traffic = slower traffic. Team size has a similar, very well-known effect.
  • Dependencies are not your friend. If adding a new feature involves every team, you’re going to be doing a lot of waiting.
  • Remember that traffic lights exist for a reason. The speed demons in our teams are as likely to cause accidents as the speed demons on our roads. When unblocking processes, remember to make sure safety isn’t compromised. Not testing code before a release might seem faster…
  • And don’t forget – it’s the speed of the system we’re optimising. Focus more attention on that, not on individual developers.

If you’re serious about building your team’s capability to rapidly, reliably and sustainably evolve software to meet rapidly changing business needs, my Code Craft and Test-Driven Development live remote training workshops are HALF PRICE until March 31st 2025.

The A-Z of Code Craft – M is for Modularity

Image

The ultimate goal of code craft is to be able to rapidly and sustainably evolve working software to meet rapidly changing business needs.

The key to this is short delivery lead times, and the key to that is making sure the software’s shippable at any time.

The key to software being shippable at any time is continuous testing, and the key to continuous testing is tests that run very fast.

If it takes 8 hours to sufficiently test the software, we’re at least 8 hours away from it being shippable (in practice – because you can introduce a lot of bugs in 8 hours – a lot longer).

If it takes 80 seconds, then a potential release is much, much closer to hand (and the bug count’s likely to be much, much lower – it’s a win-win).

So fast automated tests are the key to agility. And the key to fast automated tests is good separation of concerns in the architecture; otherwise known as modularity. (See “E is for Encapsulation“)

In an effectively modular design, different aspects of the system can be changed, reused and – most importantly – tested without needing to change, reuse or test other aspects. So we can test the calculation of the mortgage interest rate without having to involve, say, a database or a UI in that test.

Most programming languages and tech stacks have their mechanisms for encapsulating code at multiple scales from individual functions or classes, all the way up to distributed services and systems of systems. But the same principles apply at every level.

Well-designed modules:

  • Have one reason to change
  • Hide their internal workings
  • Are easily swappable
  • Have interfaces designed for the cient’s needs (not “What does this module do?”, but “What does the client need to tell it to do?”)

In particular, when it comes to achieving test suites where the vast majority run very fast, we need to cleanly separate our application’s logic from external concerns like accessing files or databases, calling web services, and so on. (See “H is for Hexagonal“).

I’ll repeat it one more time, for the folks at the back: the key to agility is modularity.


If you’re serious about building your team’s capability to rapidly, reliably and sustainably evolve software to meet rapidly changing business needs, my Code Craft and Test-Driven Development live remote training workshops are HALF PRICE until March 31st 2025.

The A-Z of Code Craft – L is for Liskov

Image

One of the big benefits of modular software design is that it enables us to change systems by swapping module implementations instead of rewriting existing modules. But that requires us to take care to ensure new implementations satisfy the original contract for using that module’s services, or we’ll break client modules.

The Liskov Substitution Principle (the L in SOLID, named after computer scientist Barbara Liskov) states that an instance of any type can be substituted with an instance of any of its subtypes.

The LSP is often thought of as an object-oriented design principle, but, in practice, it applies to any mechanism of substitution in software design, including subclasses, interfaces, function pointers, web services, and so on.

When we, for example, substitute a different implementation of an API that breaks the original contract, we contravene Gorman’s First Law of Software Development:

“Thou shalt not break shit that was working”

LSP doesn’t just work across type relationships. It also works across versions. I see teams spending a lot of time fixing code that was broken by new releases of dependencies. And I mean a lot of time.

An extension of the LSP could state something like “A version of a component can be substituted with any newer version”. Another term for this is “backwards-compatibility”.

More often than not, teams are thoughtless about backwards-compatibility; routinely breaking contracts without realising they’re doing it.

A technique that’s gaining in popularity is contract testing. This involves creating two different set-ups for the same set of automated tests; one that stubs and mocks external dependencies, and one that uses the real end points. If the stubbed and mocked tests are all passing, but the tests using the end points suddenly start failing, that suggests something’s changed at the other end.

The biggest pay-off comes when the API team can run the client team’s contract tests themselves before releasing, giving them a heads-up that they’ve broken Gorman’s First Law.


If you’re serious about building your team’s capability to rapidly, reliably and sustainably evolve software to meet rapidly changing business needs, my Code Craft and Test-Driven Development live remote training workshops are HALF PRICE until March 31st 2025.

The A-Z of Code Craft – K is for K.I.S.S.

Image

If the value created by code was a superhero, complexity would be its Kryptonite. The more complex our code is, the longer it takes to deliver, the harder it is to test, the more likely it is to have bugs, the harder it is to understand, and the more it costs to change.

For all these reasons, we should strive to keep our code as simple as possible (and no simpler). The design mantra we recite is “Keep It Simple, Stupid!” (KISS).

We might think that simpler code will be easier to write, but it turns out that, quite often, simpler is harder.

It takes more thought. It takes more discipline. And it requires us both to let go of preconceived ideas about design, and to let go of our egos. A lot of complexity in code is accidental – we just didn’t think of a simpler way. But a lot is also deliberately created to impress, in the mistaken belief that more sophisticated code means a more sophisticated coder.

And, let’s be honest now, a lot of code just isn’t needed to provide the user outcomes we want to achieve.

In the equation of “value created – cost”, every additional line of code, every additional branch, every additional abstraction, every additional dependency erodes the profit margin of the work we do.

The aim of the game of software development is to create maximum value at minimum cost, and we therefore need to be both always seeking more value, and always seeking the simplest route to unlocking it. What’s the least we can do that will work?

So, the best code crafters continuously monitor the complexity of the software, continuously seek feedback on code quality, continuously refactor to remove accidental complexity, continuously question whether complexity is really needed, and – most important of all – continuously keep one eye on the prize.


If you’re serious about building your team’s capability to rapidly, reliably and sustainably evolve software to meet rapidly changing business needs, my Code Craft and Test-Driven Development live remote training workshops are HALF PRICE until March 31st 2025.

The A-Z of Code Craft – J is for Just-In-Time

Image

Supermarkets are famously cashflow businesses. They don’t buy a million bottles of shampoo and stick them in a warehouse to sell throughout the year. They buy just enough bottles for the week, and those bottles will find their way on to shelves and into shoppers’ baskets very quickly.

Their supply chains are what they call “just-in-time”. The product goes from the supplier’s factory to the supermarket checkout as quickly as possible. Their systems and processes are streamlined to make lead times short.

The amount of money a business has invested in, say, stock or parts or ingredients is called “working capital”. Businesses of all kinds seek to minimise their working capital and to maximise their cashflow so there’s enough money to “keep the lights on” while they realise the return on their investment.

Software development’s an investment that our customers hope to see a return on in a similar way. And the flow of that return can be crucial to keeping the lights on. I’ve seen a lot of start-ups fail, not because what they were creating had no value, but because they ran out of cash to pay staff and creditors before that value could be realised.

The working capital in software development – the shampoo sitting in warehouses, if you like – is all the work that the development team has done that has yet to make it into the hands of users. Software that can’t be used has no value. Let’s call it “work in progress”.

Development teams who care about keeping the lights on seek to minimise the amount of work in progress, and to maximise the flow of value out into the business. They don’t design and build 50 features and then release the software after 12 months. They design and build one feature and release that as soon as it’s ready to be consumed. From the factory to the checkout lickety-split!

And in the same way a supermarket’s supply chains are optimised to get the product from the supplier to the checkout as soon as possible, the best dev teams optimise their processes to make the lead times on getting feature and change requests into production as short as possible, and reliably as possible.

Just-in-time delivery processes have another major advantage. If your supermarket bought a year’s supply of a particular brand of shampoo, and halfway through the year a new brand is launched with a massive ad campaign, you’re potentially stuck with a tonne of bottles that are suddenly “so last year”. If you buy them one week’s worth at a time, you can switch brands and capitalise on the buzz.

The practices of code craft – Continuous Testing, Continuous Integration, Test-Driven Development, Refactoring, Modular Design – are enablers of limiting work in progress, shrinking lead times, maximising the flow of value and improving responsiveness to changing needs.


If you’re serious about building your team’s capability to rapidly, reliably and sustainably evolve software to meet rapidly changing business needs, my Code Craft and Test-Driven Development live remote training workshops are HALF PRICE until March 31st 2025.