Codemanship Code Craft Videos

Over the last 6 months, I’ve been recording hands-on tutorials about code craft – TDD, design principles, refactoring, CI/CD and more – for the Codemanship YouTube channel.

I’ve recorded the same tutorials in JavaScript, Java, C# and (still being finished) Python.

As well as serving as a back-up for the Codemanship Code Craft training course, these series of videos forms possibly the most comprehensive free learning resource on the practices of code craft available anywhere.

Each series has over 9 hours of video, plus links to example code and other useful resources.

Codemanship Code Craft videos currently available

I’ve heard from individual developers and teams who’ve been using these videos as the basis for their practice road map. What seems to work best is to watch a video, and then straight away try out the ideas on a practical example (e.g., a TDD kata or a small project) to see how they can work on real code.

In the next few weeks, I’ll be announcing Codemanship Code Craft Study Groups, which will bring groups of like-minded learners together online once a week to watch the videos and pair program on carefully designed exercises with coaching from myself.

This will be an alternative way of receiving our popular training, but with more time dedicated to hands-on practice and coaching, and more time between lessons for the ideas to sink in. It should also be significantly less disruptive than taking a whole team out for 3 days for a regular training course, and significantly less exhausting than 3 full days of Zoom meetings! Plus the price per person will be the same as the regular Code Craft course.

Modularity & Readability

One thing I hear very often from less experienced developers is how difficult it can be for them to understand modular code.

The principles of modular design – that modules should:

  • Do one job
  • Hide their inner workings
  • Have swappable dependencies

– tend to lead to code that’s composed of small pieces that bind to abstractions for the other modules they use to do their jobs.

The key to making this work in practice rests on two factors: firstly, that developers get good at naming the boxes clearly enough so that people don’t have to look inside them to understand what they do, and secondly that developers accustom themselves to reading highly-composed code. More bluntly, developers have to learn how to read and write modular code.

Schools, universities and code clubs generally don’t get as far as modularity when they teach programming. Well, they may teach the mechanics of declaring and using modules, but they don’t present students with much opportunity to write larger, composed systems. The self-contained nature of programming problems in education typically presents students with algorithms whose implementations are all laid out on the screen in front of them.

Software at scale, though, doesn’t fit on a screen. They are jigsaw puzzles, and much more attention to how the pieces fit together is needed. Software design grows from being about algorithms and program flow to being about relationships between parts, at multiple levels of code organisation.

In this sense, young programmers leave school like freshly-minted playwrights who’ve only ever written short monologues. They know nothing of character, or motivation, or dialogue, of plotting, or pacing, or the three-act structure, nor have they ever concerned themselves with staging and practical considerations that just don’t come up when a single actor reads a single page standing centre-stage under a single spotlight.

Then they get their first job as a script assistant on a production of Noises Off and are all like “What are all these ‘stage directions’? Why are there so many scenes? I can’t follow this plot. Can we have just one character say all the lines?”

Here’s the thing; reading and writing modular code is an acquired skill. It doesn’t just happen overnight. As more and more young developers flood into the industry, I see more and more teams full of people who are easily bamboozled by modular, composed code.

Readability is about the audience. Programmers have a “reading age” defined by their ability to understand code, and code needs to be pitched to the audience’s reading age. This means that we may have to sacrifice some modularity for teams of less experienced developers. They’re not ready for it yet.

Having said all of that, of course, we get better at reading by being challenged. If we only ever read books that contained words we already know, we’d learn no new words.

I learned to read OO code by reading OO code written by more experienced programmers than me. They simultaneously pitched the code to be accessible to my level of understanding, and also a very little out of my current reach so that I had to stretch to follow the logic.

I know I’m a broken record on this topic, but that’s where mentoring comes in. Yes, there are many, many developers who lack the ability to read and write modular code. But every one of those teams could have someone who has lots of experience writing modular code who can challenge and guide them and bring them along over time – until one day it’s their turn to pay it forward.

The woeful lack of structured mentoring in our profession means that many developers go their entire careers never learning this skill. A lack of understanding combined with a lot of experience can be a dangerous mixture. “It is not that I don’t understand this play. This play is badly written. Good plays have a single character who stands in the centre of the stage under a single spotlight and reads out a 100-page monologue. Always.”

For those developers, a late-career Damascene conversion is unlikely to happen. I wish them luck.

For new developers, though, there’s a balance to be struck between working at a level they’re comfortable with today, and moving them forward to reading and writing more modular code in the future. Every program we write is both a solution to a problem today, and a learning experience to help us write a better solution tomorrow.

Developer Coaching for Beginners

Yes, the title of this blog post is a little facetious, because the reality is that at many points in our careers – at many points in the average programmer’s week – we are coaching developers.

There’s a tale – possibly apocryphal – of a programmer who, when stuck on a problem, would explain it to a large inflatable parrot. The act of articulating the problem to someone else – even a parrot – often helps us to see a solution.

Yesterday, I paired with someone working on a library to compute visual flow. Machine vision is far from my area of expertise, so how could I possibly be of any help to someone – a PhD student in machine vision, no less – working on a problem I don’t understand?

The fact is, I coach developers working in problem domains I don’t understand all the time. I’m parachuted in – often at short notice – to pair with developers who are stuck on problems that I’m not familiar with. But if you think the role of a developer coach is to solve the coachee’s problem, then you’ve not understand my problem domain.

The trick to coaching developers is not to be a font of all knowledge. If a developer wants to know something specific, they can Google it. Rather, it is to help them articulate the problem. Yes, I am an inflatable parrot.

Don’t get me wrong: this is not an unskilled role. Coaches are perhaps more like counselors than inflatable parrots. But the essence of it is the same – to get them to articulate the problem they’re trying to solve, and to encourage them to envision a solution to try out.

This does require enough knowledge of programming, of practices, of processes, and of technology, to ask the right kind of questions. “And how does that make you feel?” won’t open the right doors in a programming context.

And, naturally, as a trainer in developer practices like TDD and refactoring, I also focus on keeping you safe while you go through the process of solving your problem.

I’ll notice if you’re not writing tests. I’ll notice if you’re making a lot changes before you run your tests again. I’ll notice if you haven’t checked your code in for an hour. I’ll notice when methods are getting too big, or when there’s a bit too much duplication, or when your method has Feature Envy for another class, and I will nudge you back to the safe path of frequent testing, frequent refactoring, and continuous integration.

None of these things will solve your problem, but they will protect you from car crashes while you solve it yourself. Yes. I’m not just an inflatable parrot – I’m an inflatable parrot who makes sure you’re wearing your seat belt.

Through Codemanship, my primary focus is on helping developers to build those good habits. You solve your problem. I help you develop the skills to do it safely and sustainably. My role as coach is not just to help you to solve your problem, but to help you emerge from that process a different programmer than when you went in.

I’m an inflatable parrot with an agenda.

Yesterday, I encouraged my pairing partner to write down in a single sentence what problem they wanted to solve. And then I asked them to write down the function or feature that would solve the problem – what would it do? That led us to tests that would fail because the code currently doesn’t do what he wants, and the act of writing those tests opened up a bunch of doors towards new ways of making it do it. We didn’t solve the problem in our session, but we constrained it with executable tests: the solution must fit in this box. And that’s progress.

Now think back to your working week: how many times did you play the inflatable parrot for a colleague? I’m guessing at least once. And that makes you a developer coach. Like I said, not an unskilled role, and it takes experience and quite a bit of confidence to get to a point where you can be parachuted into almost any problem or technology domain.

But – at a fundamental level – we’re all coaches.

 

The Experience Paradox

One sentiment I hear very often from managers is how very difficult it is to hire experienced developers. This, of course, is a self-fulfilling prophecy. If you won’t hire developers without experience, how will inexperienced developers get the jobs they need to gain that experience?

I simultaneously hear from new developers – recent graduates, boot camp survivors, and so on – that they really struggle to land that first development job because they don’t have the experience.

When you hear both of these at the same time, it becomes a conversation. Unfortunately, it’s a conversation conducted from inside soundproof booths, and I’m seeing no signs that this very obvious situation will be squared any time soon.

I guess we have to add it to the list of things that every knows is wrong, but everyone does anyway. (Like adding more developers to teams when the schedule’s slipping.)

Organisations should hire for potential as well as experience. People who have the potential to be good developers can learn from people with experience. It’s a match made in heaven, and the end result is a larger pool of experienced developers. This problem can fix it itself, if we were of a mind to let it. We all know this. So why don’t we do it?

At the other end of the spectrum, I hear many, many managers say “This person is too experienced to be a developer”. And at the same time, I hear many, many very experienced developers struggle to find work. This, too, is a problem that creates itself. Typically, there are two reasons why managers rule out the most experienced developers:

  • They expect to get paid more (because they usually achieve more)
  • They won’t put up with your bulls**t

Less experienced developers may be more malleable in terms of how much – or little – you can pay them, how much unpaid overtime they may be willing to tolerate, and how willing they might be to cut corners when ordered to. They may have yet to learn what their work is really worth to you. They may have yet to experience burnout. They may yet to have lived with a large amount of technical debt.

Developers with 20+ years experience, who’ve been around the block a few times and know the score, don’t fit it into the picture of developers as fungible resources.

By freezing out inexperienced developers and very experienced developers, employers create the exact situation they complain endlessly about – lack of experienced developers. If it were my company and my money on the line, I’d hire developers with decades of experience specifically to mentor the inexperienced developers with potential I’d also be hiring.

Many employers, of course, argue that training up developers is too much of a financial risk. Once they’re good enough, won’t they leave for a better job? The clue is in the question, though. They leave for a better job. Better than the one you’re offering them once they qualify. Don’t just be a stepping stone, be a tropical island – a place they would want to stay and work.

If you think training up developers is going to generate teams of cheaper developers who’ll work harder and longer for less, then – yes – they’ll leave at the first opportunity. Finding a better job won’t be hard.

Codemanship’s Code Craft Road Map

One of the goals behind my training courses is to help developers navigate all the various disciplines of what we these days call code craft.

It helps me to have a mental road map of these disciplines, refined from three decades of developing software professionally.

codecraftroadmap

When I posted this on Twitter, a couple of people got in touch to say that they find it helpful, but also that a few of the disciplines were unfamiliar to them. So I thought it might be useful to go through them and summarise what they mean.

  • Foundations – the core enabling practices of code craft
    • Unit Testing – is writing fast-running automated tests to check the logic of our code, that we can run many times a day to ensure any changes we’ve made haven’t broken the software. We currently know of no other practical way of achieving this. Slow tests cause major bottlenecks in the development process, and tend to produce less reliable code that’s more expensive to maintain. Some folk say “unit testing” to mean “tests that check a single function, or a single module”. I mean “tests that have no external dependencies (e.g., a database) and run very fast”.
    • Version Control – is seat belts for programmers. The ability to go back to a previous working version of the code provides essential safety and frees us to be bolder with our code experiments. Version Control Systems these days also enable more effective collaboration between developers working on the same code base. I still occasionally see teams editing live code together, or even emailing source files to each other. That, my friends, is the hard way.
    • Evolutionary Development – is what fast-running unit tests and version control enable. It is one or more programmers and their customers collectively solving problems together through a series of rapid releases of a working solution, getting it less wrong with each pass based on real-world feedback. It is not teams incrementally munching their way through a feature list or any other kind of detailed plan. It’s all about the feedback, which is where we learn what works and what doesn’t. There are many takes on evolutionary development. Mine starts with a testable business goal, and ends with that goal being achieved. Yours should, too. Every release is an experiment, and experiments can fail. So the ability to revert to a previous version of the code is essential. Fast-running unit tests help keep changes to code safe and affordable. If we can’t change the code easily, evolution stalls. All of the practices of code craft are designed to enable rapid and sustained evolution of working software. In short, code craft means more throws of the dice.
  • Team Craft – how developers work together to deliver software
    • Pair Programming – is two programmers working side-by-side (figuratively speaking, because sometimes they might not even be on the same continent), writing code in real time as a single unit. One types the code – the “driver” – and one provides high-level directions – the “navigator”. When we’re driving, it’s easy to miss the bigger picture. Just like on a car journey, in the days before GPS navigation. The person at the wheel needs to be concentrating on the road, so a passenger reads the map and tells them where to go. The navigator also keeps an eye out for hazards the driver may have missed. In programming terms, that could be code quality problems, missing tests, and so on – things that could make the code harder to change later. In that sense, the navigator in a programming pair acts as a kind of quality gate, catching problems the driver may not have noticed. Studies show that pair programming produces better quality code, when it’s done effectively. It’s also a great way to share knowledge within a team. One pairing partner may know, for example, useful shortcuts in their editor that the other doesn’t. If members of a team pair with each other regularly, soon enough they’ll all know those shortcuts. Teams that pair tend to learn faster. That’s why pairing is an essential component of Codemanship training and coaching. But I appreciate that many teams view pairing as “two programmers doing the work of one”, and pair programming can be a tough sell to management. I see it a different way: for me, pair programming is two programmers avoiding the rework of seven.
    • Mob Programming – sometimes, especially in the early stages of development, we need to get the whole team on the same page. I’ve been using mob programming – where the team, or a section of it, all work together in real-time on the same code (typically around a big TV or projector screen) – for nearly 20 years. I’m a fan of how it can bring forward all those discussions and disagreements about design, about the team’s approach, and about the problem domain, airing all those issues early in the process. More recently, I’ve been encouraging teams to mob instead of having team meetings. There’s only so much we can iron out sitting around a table talking. Eventually, I like to see the code. It’s striking how often debates and misunderstandings evaporate when we actually look at the real code and try our ideas for real as a group. For me, the essence of mob programming is: don’t tell me, show me. And with more brains in the room, we greatly increase the odds that someone knows the answer. It’s telling that when we do team exercises on Codemanship workshops, the teams that mob tend to complete the exercises faster than the teams who work in parallel. And, like pair programming, mobbing accelerates team learning. If you have junior or trainee developers on your team, I seriously recommend regular mobbing as well as pairing.
  • Specification By Example – is using concrete examples to drive out a precise understanding of what the customer needs the software to do. It is practiced usually at two levels of abstraction: the system, and the internal high-level design of the code.
    • Test-Driven Development – is using tests (typically internal unit tests) to evolve the internal design of a system that satisfies an external (“customer”) test. It mandates discovery of internal design in small and very frequent feedback loops, making a few design decisions in each feedback loop. In each feedback loop, we start by writing a test that fails, which describes something we need the code to do that it currently doesn’t. Then we write the simplest solution that will pass that test. Then we review the code and make any necessary improvements – e.g. to remove some duplication, or make the code easier to understand – before moving on to the next failing test. One test at a time, we flesh out a design, discovering the internal logic and useful abstractions like methods/functions, classes/modules, interfaces and so on as we triangulate a working solution. TDD has multiple benefits that tend to make the investment in our tests worthwhile. For a start, if we only write code to pass tests, then at the end we will have all our solution code covered by fast-running tests. TDD produces high test assurance. Also, we’ve found that code that is test-driven tends to be simpler, lower in duplication and more modular. Indeed, TDD forces us to design our solutions in such a way that they are testable. Testable is synonymous with modular. Working in fast feedback loops means we tend to make fewer design decisions before getting feedback, and this tends to bring more focus to each decision. TDD, done well, promotes a form of continuous code review that few other techniques do. TDD also discourages us from writing code we don’t need, since all solution code is written to pass tests. It focuses us on the “what” instead of the “how”. Overly complex or redundant code is reduced. So, TDD tends to produce more reliable code (studies find up to 90% less bugs in production), that can be re-tested quickly, and that is simpler and more maintainable. It’s an effective way to achieve the frequent and sustained release cycles demanded by evolutionary development. We’ve yet to find a better way.
    • Behaviour-Driven Development – is working with the customer at the system level to precisely define not what the functions and modules inside do, but what the system does as a whole. Customer tests – tests we’ve agreed with our customer that describe system behaviour using real examples (e.g., for a £250,000 mortgage paid back over 25 years at 4% interest, the monthly payments should be exactly £1,290) – drive our internal design, telling us what the units in our “unit tests” need to do in order to deliver the system behaviour the customer desires. These tests say nothing about how the required outputs are calculated, and ideally make no mention of the system design itself, leaving the developers and UX folk to figure those design details out. They are purely logical tests, precisely capturing the domain logic involved in interactions with the system. The power of BDD and customer tests (sometimes called “acceptance tests”) is how using concrete examples can help us drive out a shared understanding of what exactly a requirement like “…and then the mortgage repayments are calculated” really means. Automating these tests to pull in the example data provided by our customer forces us to be 100% clear about what the test means, since a computer cannot interpret an ambiguous statement (yet). Customer tests provide an outer “wheel” that drives the inner wheel of unit tests and TDD. We may need to write a bunch of internal units to pass an external customer test, so that outer wheel will turn slower. But it’s important those wheels of BDD and TDD are directly connected. We only write solution code to pass unit tests, and we only write unit tests for logic needed to pass the customer test.
  • Code Quality – refers specifically to the properties of our code that make it easier or harder to change. As teams mature, their focus will often shift away from “making it work” to “making it easier to change, too”. This typically signals a growth in the maturity of the developers as code crafters.
    • Software Design Principles – address the underlying factors in code mechanics that can make code harder to change. On Codemanship courses, we teach two sets of design principles: Simple Design and Modular Design.
      • Simple Design
        • The code must work
        • The code must clearly reveal it’s intent (i.e., using module names, function names, variable names, constants and so on, to tell the story of what the code does)
        • The code must be low in duplication (unless that makes it harder to understand)
        • The code must be the simplest thing that will work
      • Modular Design (where a “module” could be a class, or component, or a service etc)
        • Modules should do one job
        • Modules should know as little about each other as possible
        • Module dependencies should be easy to swap
    • Refactoring – is the discipline of improving the internal design of our software without changing what it does. More bluntly, it’s making the code easier to change without breaking it. Like TDD, refactoring works in small feedback cycles. We perform a single refactoring – like renaming a class – and then we immediately re-run our tests to make sure we didn’t break anything. Then we do another refactoring (e.g., move that class into a different package) and test again. And then another refactoring, and test. And another, and test. And so on. As you can probably imagine, a good suite of fast-running automated tests is essential here. Refactoring and TDD work hand-in-hand: the tests make refactoring safer, and without a significant amount of refactoring, TDD becomes unsustainable. Working in these small, safe steps, a good developer can quite radically restructure the code whilst ensuring all along the way that the software still works. I was very tempted to put refactoring under Foundation, because it really is a foundational discipline for any kind of programming. But it requires a good “nose” for code quality, and it’s also an advanced skill to learn properly. So I’ve grouped it here under Code Quality. Developers need to learn to recognise code quality problems when they see them, and get hundreds of hours of practice at refactoring the code safely to eliminate them.
    • Legacy Code – is code that is in active use, and therefore probably needs to be updated and improved regularly, but is too expensive and risky to change. This is usually because the code lacks fast-running automated tests. To change legacy code safely, we need to get unit tests around the parts of the code we need to change. To achieve that, we usually need to refactor that code to make it easy to unit test – i.e., to remove external dependencies from that code. This takes discipline and care. But if every change to a legacy system started with these steps, over time the unit test coverage would rise and the internal design would become more and more modular, making changes progressively easier. Most developers are afraid to work on legacy code. But with a little extra discipline, they needn’t be. I actually find it very satisfying to rehabilitate software that’s become a millstone around our customers’ necks. Most code in operation today is legacy code.
    • Continuous Inspection – is how we catch code quality problems early, when they’re easier to fix. Like anything with the word “continuous” in the title, continuous inspection implies frequent automated checking of the code for cod quality “bugs” like functions that are too big or too complicated, modules with too many dependencies and so on. In traditional approaches, teams do code reviews to find these kinds of issues. For example, it’s popular these days to require a code review before a developer’s changes can be merged into the master branch of their repo. This creates bottlenecks in the delivery process, though. Code reviews performed by people looking at the code are a form of manual testing. You have to wait for someone to be available to do it, and it may take them some time to review all the changes you’ve made. More advanced teams have removed this bottleneck by automating some or all of their code reviews. It requires some investment to create an effective suite of code quality gates, but the pay-off in speeding up the check-in process usually more than pays for it. Teams doing continuous inspection tend to produce code of a significantly higher quality than teams doing manual code reviews.
  • Software Delivery – is all about how the code we write gets to the operational environment that requires it. We typically cover it in two stages: how does code get from the developer’s desktop into a shared repository of code that could be built, tested and released at any time? And how does that code get from the repository onto the end user’s smartphone, or the rented cloud servers, or the TV set-top box as a complete usable product?
    • Continuous Integration – is the practice of developers frequently (at least once a day) merging their changes into a shared repository from which the software can be built, tested and potentially deployed. Often seen as purely a technology issue – “we have a build server” – CI is actually a set of disciplines that the technology only enables if the team applies them. First, it implies that developers don’t go too long before merging their changes into the same branch – usually the master branch or “trunk”. Long-lived developer branches – often referred to as “feature branches” – that go unmerged for days prevent frequent merging of (and testing of merged) code, and is therefore most definitely not CI. The benefit of frequent tested merges is that we catch conflicts much earlier, and more frequent merges typically means less changes in each merge, therefore less merge conflicts overall. Teams working on long-lived branches often report being stuck in “merge hell” where, say, at the end of the week everyone in the team tries to merge large batches of conflicting changes. In CI, once a developer has merged their changes to the master-branch, the code in the repo is built and the tests are run to ensure none of those changes has “broken the build”. It also acts as a double-check that the changes work on a different machine (the build server), which reduces the risk of configuration mistakes. Another implication of CI – if our intent is to have a repository of code that can be deployed at any time – is that the code in master branch must always work. This means that developers need to check before they merge that the resulting merged code will work. Running a suite of good automated tests beforehand helps to ensure this. Teams who lack those tests – or who don’t run them because they take too long – tend to find that the code in their repo is permanently broken to some degree. In this case, releases will require a “stabilisation” phase to find the bugs and fix them. So the software can’t be released as soon as the customer wants.
    • Continuous Delivery – means ensuring that our software is always shippable. This encompasses a lot of disciplines. If the is code sitting on developers’ desktops or languishing in long-lived branches, we can’t ship it. If the code sitting in our repo is broken, we can’t ship it. If there’s no fast and reliable way to take the code in the repo and deploy it as a working end product to where it needs to go, we can’t ship it. As well as disciplines like TDD and CI, continuous delivery also requires a very significant investment in automating the delivery pipeline – automating builds, automating testing (and making those test run fast enough), automating code reviews, automating deployments, and so on. And these automated delivery processes need to be fast. If your builds take 3 hours – usually because the tests take so long to run – then that will slow down those all-important customer feedback loops, and slow down the process of learning from our releases and evolving a better design. Build times in particular are like the metabolism of your development process. If development has a slow metabolism, that can lead to all sorts of other problems. You’d be surprised how often I’ve seen teams with myriad difficulties watch those issues magically evaporate after we cut their build+test time down from hours to minutes.

Now, most of this stuff is known to most developers – or, at the very least, they know of them. The final two headings caused a few scratched heads. These are more advanced topics that I’ve found teams do need to think about, but usually after they’ve mastered the core disciplines that come before.

  • Managing Code Craft
    • The Case for Code Craft – acknowledges that code craft doesn’t exist in a vacuum, and shouldn’t be seen as an end in itself. We don’t write unit tests because, for example, we’re “professionals”. We write unit tests to make changing code easier and safer. I’ve found it helps enormously to both be clear in my own mind about why I’m doing these things, as well as in persuading teams that they should try them, too. I hear it from teams all the time: “We want to do TDD, but we’re not allowed”. I’ve never had that problem, and my ability to articulate why I’m doing TDD helps.
    • Code Craft Metrics – once you’ve made your case, you’ll need to back it up with hard data. Do the disciplines of code craft really speed up feedback cycles? Do they really reduce bug counts, and does that really save time and money? Do they really reduce the cost of changing code? Do they really help us to sustain the pace of innovation for longer? I’m amazed how few teams track these things. It’s very handy data to have when the boss comes a’knockin’ with their Micro-Manager hat on, ready to tell you how to do your job.
    • Scaling Code Craft – is all about how code craft on a team and within a development organisation just doesn’t magically happen overnight. There are lots of skills and ideas and tools involved, all of which need to be learned. And these are practical skills, like riding a bicycle. You can;t just read a book and go “Hey, I’m a test-driven developer now”. Nope. You’re just someone who knows in theory what TDD is. You’ve got to do TDD to learn TDD, and lot’s of it. And all that takes time. Most teams who fail to adopt code craft practices do so because they grossly underestimated how much time would be required to learn them. They approach it with such low “energy” that the code craft learning curve might as well be a wall. So I help organisations structure their learning, with a combination of reading, training and mentoring to get teams on the same page, and peer-based practice and learning. To scale that up, you need to be growing your own internal mentors. Ad hoc, “a bit here when it’s needed”, “a smigen there when we get a moment” simply doesn’t seem to work. You need to have a plan, and you need to invest. And however much you were thinking of investing, it’s not going to be enough.
  • High-Integrity Code Craft
    • Load-Bearing Code – is that portion of code that we find in almost any non-trivial software that is much more critical than the rest. That might be because it’s on an execution path for a critical feature, or because it’s a heavily reused piece of code that lies on many paths for many features. Most teams are not aware of where their load-bearing code is. Most teams don’t give it any thought. And this is where many of the horror stories attributed to bugs in software begin. Teams can improve at identifying load-bearing code, and at applying more exhaustive and rigorous testing techniques to achieve higher levels of assurance when needed. And before you say “Yeah, but none of our code is critical”, I’ll bet a shiny penny there’s a small percentage of your code that really, really, really needs to work. It’s there, lurking in most software, just waiting to send that embarrassing email to everyone in your address book.
    • Guided Inspection – is a powerful way of testing code by reading it. Many studies have shown that code inspections tend to find more bugs than any other kind of testing. In guided inspections, we step through our code line by line, reasoning about what it will do for a specific test case – effectively executing the code in our heads. This is, of course, labour-intensive, but we would typically only do it for load-bearing code, and only when that code itself has changed. If we discover new bugs in an inspection, we feed that back into an automated test that will catch the bug if it ever re-emerges, adding it to our suite of fast-running regression tests.
    • Design By Contract – is a technique for ensuring the correctness of the interactions between components of our system. Every interaction has a contract: a pre-condition that describes when a function or service can be used (e.g., you can only transfer money if your account has sufficient funds), and a post-condition that describes what that function or service should provide to the client (e.g., the money is deducted from your account and credited to the payee’s account). There are also invariants: things that must always be true if the software is working as required (e.g., your account never goes over it’s limit). Contracts are useful in two ways: for reasoning about the correct behaviour of functions and services, and for embedding expectations about that behaviour inside the code itself as assertions that will fail during testing if an expectation isn’t satisfied. We can test post-conditions using traditional unit tests, but in load-bearing code, teams have found it helpful to assert pre-conditions to ensure that not only do functions and services do what they’re supposed to, but they’re only ever called when they should be. DBC presents us with some useful conceptual tools, as well as programming techniques when we need them. It also paves the way to a much more exhaustive kind of automated testing, namely…
    • Property-Based Testing – sometimes referred to as generative testing, is a form of automated testing where the inputs to the tests themselves are programmatically calculated. For example, we might test that a numerical algorithm works for a range of inputs from 0…1000, at increments of 0.01. or we might test that a shipping calculation works for all combinations of inputs of country, weight class and mailing class. This is achieved by generalising the expected results in our tests, so instead of asserting that the square root of 4 is 2, we might assert that the square root of any positive number multiplied by itself is equal to the original number. These properties of correct test results look a lot like the contracts we might write when we practice Design By Contract, and therefore we might find experience in writing contracts helpful in building that kind of declarative style of asserting. The beauty of property-based tests is that they scale easily. Generating 1,000 random inputs and generating 10,000 random inputs requires a change of a single character in our test. One character, 9,000 extra test cases. Two additional characters (100,000) yields 99,000 more test cases. Property-based tests enable us to achieve quite mind-boggling levels of test assurance with relatively little extra test code, using tools most developers already know.

So there you have it: my code craft road map, in a nutshell. Many of these disciplines are covered in introductory – but practical – detail in the Codemanship TDD course book

If your team could use a hands-on introduction to code craft, our 3-day hands-on TDD course can give them a head-start.

4 Out Of 5 Developers Would Choose To Stay Developers. Is It Time We Let Them?

Following on from yesterday’s post about squaring the circle of learning and mentoring in software development, a little poll I ran on Twitter clearly shows that a large majority of software developers would prefer to stay hands-on if they had the choice.

I’ve seen many developers over my 28-year career reluctantly pushed into management roles, and heard so very many talk about how much they miss making software with their own hands. But in too many organisations, the only way to progress in terms of seniority and pay is to move away from code.

Some choose not to progress, making do with the pay and the authority of a developer and biting their tongues when managers who haven’t touched code in years tell them to do silly things. But then we often find that ageism starts to kick in eventually, making it harder and harder to get hired in those hands-on roles. “Why is she still coding?” There’s an assumption among hirers that to still be in these “less senior” roles at, say, 45 is a failure to launch, and not a success in being exactly where you want to be, doing what you love.

A conversation I had recently with a team highlighted what can go wrong when you promote your best developers into non-development positions. They found themselves have to refer technical decisions up to people who no longer had a practical grasp of the technology, and this created a huge communication overhead that wouldn’t have been necessary had the decision-making authority been given to the people responsible for making those decisions work.

I’ve always believed that authority and responsibility go hand-in-hand. Anyone who is given the responsibility for making something happen should also be given the necessary authority to decide how to make it happen.

Not all developers welcome responsibility, of course. In some large organisations, I’ve seen teams grow comfortable with the top-down bureaucracy. They get used to people making the decisions for them, and become institutionalised in much the same way soldiers or prisoners do. What’s for dinner? Whatever they give us. When do we go to bed? Whenever they say. What unit testing tool should we use? Whichever one they tell us to.

But most developers are grown-ups. In their own lives, they make big decisions all the time. They buy houses. They have kids. They choose schools. They vote. It’s pretty wretched, then, seeing teams not being trusted to even purchase laptops for themselves. When teams are trusted, and given both responsibility and authority for getting things done, they tend to rise to that.

And developers should be trusted with their own careers, too. If they were, then I suspect there’d be a lot more active coders with decades of experience to share with the ever-growing number of new developers coming into the industry.

How A Developer’s Career *Should* Work

Apprenticeships are in the news today here in the UK, with the rather shocking revelation that since the government introduced their new apprenticeship levy scheme – where employers must pay 0.5% of their income into a special government-run “bank” to be used to fund apprenticeships of all kinds – the number of people starting apprenticeships has actually fallen.

I met with the people in charge of the scheme, and was less than hopeful that it would actually work – especially in our industry. Time and again I see decision makers vastly underestimate the time and resources needed to grow a software developer, and their planned software developer apprenticeship looked lacking, too. Later, I heard from multiple employers who’d taken on dev apprentices, and they were really struggling with the lack of practical support. Who will teach these young developers? Who will mentor them? How long is it really going to take before we can leave them to work unsupervised?

The sad fact is that many of these employers just heard “cheap developers” and didn’t stop to think “Ah, but why so cheap?” The brutal answer is: because they’re not developers. Yet. The whole point of the apprenticeship is that you turn them into developers. And training a software developer takes a lot of time and lot of money.

If you saw that one house on a street was half the price of the other houses, would you not stop to ask “why?” In the case of apprentices, it’s because you only bought the land. You have to build a house on it.

The main sticking point here is that somebody who knows what they’re doing has to make themselves available to help the apprentices learn. And they need to be very available, because that requires a big investment in time.

Our industry, though, has structured itself to make this investment unworkable. The most senior developers are either too busy getting shit done, or they’re not active developers any more. With the best will in the world, no amount of transferrable skills are going to get transferred if the person who has all that useful knowledge last programmed in COBOL on an IBM mainframe. It would be like being taught economics by someone who only speaks Anglo-Saxon.

In order to square this circle, our industry needs to be restructured to make sustained, in-depth skills transfer possible.

This is how it could work:

  • At the start of a developer’s career, the most productive thing they can do with their time is learn. Career’s should start with a few months of nothing but learning. All day. Every day. A coding boot camp might be a model to follow here – provided we all acknowledge that the learning doesn’t end with boot cam graduation. It’s just a kick-start.
  • After graduating boot camp, developers become apprentices. They work on real teams doing real work 3-4 days a week, with a the other 1-2 days released for further dedicated, structured learning. This would continue for 2-3 years as they build their skills and their confidence to a point where employers feel happy leaving them to work unsupervised. It might even lead to a degree, to validate their progress.
  • Once they’ve completed their apprenticeship, developers pay their dues and return the investment employers have made in them by delivering working software of real value, while continuing to gain experience and learn more and more. There might be a decade or more of this real-world work. They continue to be mentored by more experienced developers, but in a more hands-off kind of way. A nudge here, a kind word there etc. Enlightened employers will recognise that dedicated learning time is still a wise investment, throughout a developer’s career. They may still devote 10-20% of their time to this, but at this level of achievement, it’s more like doing a PhD. We might expect developers to eventually add their own contributions to the software development landscape in this phase of their career. Maybe write a useful new tool, or invent a new technique. Maybe speak at conferences. Maybe write a book.
  • During this – and I hesitate to use this term – “journeyman” phase, developers may find they’re called upon increasingly more to mentor less experienced developers, and to share their knowledge freely. I believe this is an important part of a developer’s progress. I’ve found that what really tests my understanding of something is trying to explain it to other people. An increasing emphasis on sharing knowledge, on mentoring, and especially on leading by example, would mark the later stages of this phase.
  • Eventually, developers reach a phase in their career where the most productive use of their time is teaching. This is the “profess” in our profession. And this is where we square the circle. Who is going to do the teaching in the boot camps? Who is going to train and mentor the apprentices? Simple answer: we are.

Now, for sure, not every developer will be cut out for that, and not every developer will want to go down that route. Some will become managers, and that’s fine. We need more developers in technology management positions, frankly. But your average corporation doesn’t need 20 CTOs. It may well need 20 active mentors, though – keeping hands-on with the latest tools and technologies so they can offer practical help to 100 less experienced developers.

At present, in the vast majority of organisations, no such career path exists. We are set up to move away from the code face just at the time when we should be working side-by-side with apprentices. I had to invent that role for myself by starting Codemanship. Had such roles existed for someone with 20 years’ experience, there would have been no need. I didn’t start a business to start a business. I started a business so that – as the boss – I could offer myself my ideal job.

And, as the boss, I understand why this job is important. It’s the most useful thing I can offer at this stage in my career. This is why I believe it’s important that more bosses come from a software development background – so they can see the benefits. As it stands, employers – for the most part – just don’t get it. Yet.

There’s more at stake here than pay and perks for developers who might progress beyond the current career ceiling that too many organisations impose on people who still write code. One factor that strongly determines the way a business invests its money is who is holding the purse strings. I sometimes rail at the infantalisation of software professionals, where we must go cap in hand to “mummy and daddy” for the most insignificant of purchases. If I need a new monitor at home, I go out and buy a new monitor. Easy. In the world of corporate tech, not so easy. I recall once having multiple meetings, escalating all the way up to the Director of IT, about buying a £200 whiteboard.

If the budget holders don’t understand the technical side of things – perhaps they never did, or it was so long ago they were directly involved in technology – then it can be hard to persuade them of the benefits of an investment in tools, in books, in training, in furniture, etc. As a business owner, I experience it from the other side, watching in dismay the hoops some teams have to jump through to get things they need like training.

Codemanship training does not appeal to CTOs, on the whole. Most don’t see the benefits. They buy it because the developers tugged at their sleeve and whined and pleaded long enough that the boss realised the only way to make them shut up was to buy a course. In that sense, code craft training’s a bit like the candy they display at supermarket checkouts.

A very few more enlightened companies let their developers make those decisions themselves, giving them budgets they can spend without having to get purchases approved. But they’re in the minority. Many more teams have to crawl over broken glass to book, say, a TDD workshop.

On a larger scale, decisions about what developers’ time gets invested in are usually not in the developers’ hands. If it were up to them, I suspect we’d see more time devoted to learning, to teaching, and to mentoring. But, sadly, it’s not. They have to ask for permission – quite probably from someone who isn’t a a developer, perhaps even someone who thinks writing software is a low-status job that doesn’t warrant that kind of investment in skills.

When that changes, I believe we will finally square the circle.

Standards & Gatekeepers & Fitted Bathrooms

One thing I’ve learned from 10 years on Twitter is that whenever you dare to suggest that the software development profession should have minimum basic standards of competence, people will descend on you from a great height accusing you of being “elitist” and a “gatekeeper”.

Evil Jason wants to keep people out of software development. BAD JASON!

Well, okay: sure. I admit it. I want to keep people out of software development. Specifically, I want to keep people who can’t do the job out of software development. Mwuhahahahahaha etc.

That’s a very different proposition from suggesting that I want to stop people from becoming good, competent software developers, though. If you know me, then you know I’ve long advocated proper, long-term, in-depth paid software developer apprenticeships. I’ve advocated proper on-the-job training and mentoring. (Heck, it’s my entire business these days.) I’ve advocated schools and colleges and code clubs encouraging enthusiasts to build basic software development skills – because fundamentals are the building blocks of fun (or something pithy like that.)

I advocate every entry avenue into this profession except one – turning up claiming to be a software developer, without the basic competencies, and expecting to get paid a high salary for messing up someone’s IT.

If you can’t do the basic job yet, then you’re a trainee – an apprentice, if you prefer – software developer. And yes, that is gatekeeping. The gates to training should be wide open to anyone with aptitude. Money, social background, ethnicity, gender, sexual orientation, age or disabilities should be no barrier.

But…

I don’t believe the gates should be wide open to practicing as a software developer – unsupervised by experienced and competent mentors – on real software and systems with real end users and real consequences for the kinds of salaries we can earn – just for anyone who fancies that job title. I think we should have to earn it. I think I should have had to earn it when I started out. Crikey, the damage I probably did before I accidentally fell into a nest of experienced software engineers who fixed me…

Here’s the thing; when I was 23, I didn’t know that I wasn’t a competent software developer. I thought I was aces. Even though I’d never used version control, never written a unit test, never refactored code – not once – and thought that a 300-line function with nested IFs running 10 deep was super spiffy and jolly clever. I needed people to show me. I was lucky to find them, though I certainly didn’t seek them out.

And who the heck am I to say our profession should have gates, anyway? Nobody. I have no power over hiring anywhere. And, for sure, when I’ve been involved in the hiring process, bosses have ignored my advice many times. And many times, they’ve paid the price for letting someone who lacked basic dev skills loose on their production code. And a few times they’ve even admitted it afterwards.

But I’ve rarely said “Don’t hire that person”. Usually, I say “Train that person”. Most employers choose not to, of course. They want them ready-made and fully-formed. And, ideally, cheap. Someone else can train them. Hell, they can train themselves. And many of us do.

In that landscape, insisting on basic standards is difficult – because where do would-be professional developers go to get real-world experience, high-quality training and long-term mentoring? Would-be plumbers and would-be veterinarians and would-be hairdressers have well-defined routes from aspiration to profession. We’re still very much at the “If You Say You’re A Software Developer Then You’re A Software Developer” stage.

So that’s where we are right now. We can stay at that level, and things will never improve. Or we can do something about it. I maintain that long-term paid apprenticeships – leading to recognised qualifications – are the way to go. I maintain that on-the-job training and mentoring are essential. You can’t learn this job from books. You’ve got to see it and do it for real, and you need people around you who’ve done lots of it to guide you and set an example.

I maintain that apprenticeships and training and mentoring should be the norm for people entering the profession – be it straight of high school or after a degree or after decades of experience working in other industries or after raising children. This route should be open to all. But there should be a bar they need to jump at the end before being allowed to work unsupervised on production code. I wish I’d had that from the start. I should have had that.

And, yes, how unfair it is for someone who blundered into software development largely self-taught to look back and say “Young folk today must qualify first!” But there must have been a generation of self-taught physicians who one day declared “Okay, from now on, doctors have to qualify.” If not my generation, or your generation, then whose generation? We can’t keep kicking this can down the road forever.

As software “eats the world”, more and more people are going to enter the profession. More and more of our daily lives will be run by software, and the consequences of system failures and high costs of changing code will hurt society more and more. This problem isn’t going away.

I hope to Bod that the people coming to fit my bathroom next week don’t just say they’re builders and plumbers and electricians. I hope to Bod they did proper apprenticeships and had plenty of good training and mentoring. I hope to Bod that their professions have basic standards of competence.

And I hope to Bod that those standards are enforced by… gatekeepers.

The Mentoring Paradox

I recently ran a pair of Twitter polls asking experienced developers if mentoring was an official part of their duties, and asking inexperienced developers if they received regular dedicated mentoring.

It’s a tale in two parts: 3/4 experienced devs said mentoring was part of their job, 8/9 inexperienced devs said they don’t get regular dedicated mentoring.

At first glance, this might appear to be a paradox. But I think it can be explained with two extra pieces of information:

  • Our profession is a pyramid, with the most experienced developers greatly outnumbered by less experienced developers
  • Opinions differ widely on what we mean by “mentoring”

Some developers equate mentoring with practices like pair programming. If an experienced developer pairs with a less experienced developer, they might class that as “mentoring”. What we’ve found at Codemanship, though, is that pairing != mentoring necessarily. It’s unstructured, lacks clear goals for what the mentee needs or wants to learn, and is often done in a naive way by people who may well be technically strong but who lack mentoring skills and experience. And we also need to remember that pair programming’s still pretty rare. Most employers don’t allow it.

A lot of new developers report that pair programming with experienced developers can be a frustrating and demoralising experience. Being a great violinist doesn’t necessarily make you a good violin teacher. In a lot of cases, whatever the mentor thinks they’re doing, the mentee doesn’t see it as mentoring.

The other problem with this kind of ad hoc it’s-kind-of-mentoring-but-not-in-a-structured-way mentoring is that it promotes mostly reactive learning. Mentees learn stuff that just happens to come up. To give a developer a solid and well-rounded foundation in dev fundamentals, there needs to be a game plan, and thought needs to be put into creating the necessary learning opportunities within a reasonable timeframe. This necessitates a balance with proactive learning. Even some of the most advanced employers I speak to admit they have no such plan, and little time and resources dedicated to creating the necessary learning opportunities.

To give you an example, let’s imagine we agree it’s time a new hire learns how to refactor Feature Envy. In a reactive environment, we wait until Feature Envy crops up. In coaching developers, I’ve learned that it can be a long wait. And when it does crop up, we may be too busy or distracted dealing with the 1,001 other things we need to think about to take advantage of the opportunity. You need to be super-super on the ball. It’s far easier to enccourage the team to “bottle” code smells* before they eliminate them, so a learning opportunity like this comes ready-made and easy to locate.

*Check in the code with a commit message that identifies the location of the code smell

We found that devs learn refactoring skills much faster when the opportunities to practice come ready-made like this. (There’s also the side effect when a team does a lot of refactoring that certain code smells get eliminated completely from the code base. Like diseases we wiped out, there is value in keeping some samples in the freezer to experiment on.)

Bottling code smells takes extra thought and effort. Practicing refactoring on code smells that have already been eliminated adds no value to the current code base. Proactive learning comes at a cost that most employers are unwilling to pay. So, instead, they pay in an increased cost of changing code, with the knock-on effect that has on their business. (And I’ve seen a high cost of changing code kill some pretty big businesses.)

Effective long-term mentoring of junior developers costs time and money. There’s no way around that – no magic fix, no silver bullet. You’ll need to give junior developers time out for proactive learning. You’ll need to sacrifice the “productive” time of senior developers to provide good mentoring – which includes time to plan and prepare to mentor. (I spend a good deal of my time learning stuff so I can stay one step ahead of devs I’m mentoring – learning the shiny new languages, tools and techniques – filling the gaps in my knowledge before I try to fill the gaps in theirs.)

Nowhere is this more evident than in the UK government’s Software Developer Apprenticeship programme. While there are some shining beacons who do a superb job with apprentices, I hear from many employers who grossly underestimated the investment they’d have to make – especially in dedicated structured mentoring. There are too many places where apprentices are left to figure it out for themselves.

I would argue that possibly the most productive way experienced developers could use their time is in helping less experienced developers build their skills. At my level of experience, I choose to be almost completely dedicated to it. Devs with more than two decades of professional experience are outnumbered 13 to 1, and I’m not a 13x developer.

The way I see it, if companies are happy to promote their most experienced developers into non-technical management roles – losing most of the benefit of that experience – they might as well promote them into hands-on mentoring roles instead. Either way, less experienced developers will be writing the code. At least this way, they’ll be writing better code sooner.

I also genuinely believe that mentoring has many benefits for even the most experienced developers. I’ve had to learn a tonne of stuff specifically so I can explain and demonstrate it to someone else. And to explain it, you’ve really got to wrap your head around it. There’s all sorts of things I kind-of-sort-of understood, but not really, that I’m now 100% clear on purely because I had to get my story straight before I told it to other people. It’s taken me many years to build my Explaining Fu – and while I’m no Richard Feynman, that clarity has definitely benefitted me and my mentees. It also finds its way into my code quite often. I’m way more S.O.L.I.D. aware than I used to be, for example. That’s because I’ve done example after example after example. It’s like ear training for musicians.

These experiences have built my confidence, as well. I’ve given the fundamentals so much thought, and explained and demonstrated them so many times in front of so many very different audiences, that I feel my horizons have widened considerably. Need to learn Kotlin? No probs. Need to prepare a workshop? No worries. Need to present to the board? No sweat. I’m much more fearless after two decades of teaching and mentoring. Sure, it scared the crap out of me in 1999. In 2019, give me a spear and show me where the mammoth’s are at.

So, not only are there people out there who are better developers because of my mentoring, I’m also a better developer for it, too.

This is why I believe structured mentoring needs to be part of the developer journey. First, as a mentee, and then eventually as a dedicated mentor. Our profession needs to be structured so this is normal: the rule and not the exception.

 

If you’d like to talk about developer training and mentoring in your team or organisation, drop me a line.