This is the last in my four-part literate programming series:
- What is it?
- Problems and challenges
- Modern variants
- Closing thoughts (the rest of this article)
As I’ve developed my understanding of literate programming (LP) I’ve been increasingly struck how much…
Literate programming is like TDD
For the purposes of this discussion I’ll merge the ideas of test-driven development (TDD) and automated testing.
I’ve introduced TDD into many organisations, and inevitably have had to overcome many people’s objections. And the objections I’ve encountered for TDD are often identical, or nearly identical, to those of LP. From the “problems and challenges” article these are:
- “We have to integrate it into the our tool chain.” This is a reasonable challenge. Both TDD and LP offer tools which work in isolation, but if it’s integrated into our workflow, IDE and so on, then its use becomes much easier.
- “Our code will be obsolete soon, anyway.” That’s certainly what many of us think—or are told—when we’re encouraged to produce something quickly and maybe cut a few corners.
- “Developers are not writers (or testers).” So how can they be expected to write effective design documents/tests?
- “It takes more time.” This is certainly true. Writing code + tests, or code + design documentation, or even code + tests + design documentation all require more time than just writing code.
- “It demands discipline.” This is something of a self-deprecating objection… unless people also say, “Well I’ve got the discipline, but I’m not sure the others do.” Or we may question how much discipline we’re able to maintain when under particular pressure—which is often a reality.
- “It’s harder to revise a design story (or tests).” This is similar to “it takes more time”. If we’re revising code and supporting work, then of course that will take more time than just revising code.
- “What about boilerplate narrative (or tests)?” There is such a thing as pointless, or poor, automated tests for boilerplate code; how do we provide the required design narrative for boilerplate code?
- “How does this work in a world of objects, libraries, serverless and microservices?” We have had to develop different testing techniques for different elements of a complex system. Does one kind of design narrative, or design document, apply across such a system?
But LP isn’t just like TDD in terms of the objections. Both are intended to improve quality, albeit at different ends of the quality pipeline. Literate programming is about stopping errors going into the system in the first place; TDD is about catching them once they’ve been coded. It’s the quality/testing distinction.
TDD is often described as “executable documentation”. Literate programming is actual documentation.
Both TDD and LP require changes to our workflow. And that workflow has two parts in both cases: the creation (of the design document, or the tests) and the use (reviewing the design, or running the tests).
And—importantly—for TDD and for literate programming we can do it poorly, so we have to learn to do it well. There’s a chance to write poor automated tests, and there’s a chance to write poor design narrative (as with the boilerplate code example above, but likely on other occasions, too). How do we introduce automated tests (or LP design narrative) into an existing codebase that wasn’t designed for it? How (as above) do we use it appropriately for the world of serverless, microservices, etc? These are all things we need to learn about literate programming, just as we have learnt them for test-driven development and automated testing.
As we can see, there are many, many similarities between automated testing (and/or TDD) and literate programming. But, of course,…
They are not the same
Literate programming is about promoting and demonstrating good design. TDD doesn’t really address the design question, other than code needing to reach a certain level of decent design just to be easily testable.
Literate programming is about enabling much more effective design reviews, or at least the link between the design and the code. (LP doesn’t have much to say about the link from requirements to design as far as I can tell.) And unlike executing automated tests, that second step—the one after the writing step—is manual.
However, that “design review” step is already present in many teams’ workflow—it’s the code review that’s triggered by the pull request.
And finally, TDD is not actually literate programming, so what happens if we employ them both? Do the tests form part of our design narrative? This is part of the work required to understand how to do literate programming well in the current development era.
A way forward
In moving forward we have a selection of tools today. But we also need to counter the objections set out above, just as they were countered wherever automated testing was introduced. In brief…
- “We have to integrate it into the our tool chain.” LP tools can be used without wider integration, but they would be much more effective with it. This does require work; there’s no getting away from that. People have to develop those integrations, and it’s something of a chicken-and-egg situation.
- “Our code will be obsolete soon, anyway.” Maybe, but there’s nothing so permanent as temporary. Perhaps we should at least put some simple LP design narrative into our project, and then we can it enhance later if we discover the code starts sticking around longer than we thought.
- “Developers are not writers (or testers).” The better developers I’ve known have all been well-rounded individuals; they don’t just code, they also explain and mentor and strategise and design and plan and more. Expanding our skillset is surely a valuable thing to do. It’s about being more than just a coder. It’s part of being able to call oneself a professional.
- “It takes more time.” Yes, but we also want quality, and we’d rather spend our time developing more code than fixing problems due to poor quality. There is presumably a degree—which is more than zero—to which design narrative helps, and to which, therefore, LP is valuable.
- “It demands discipline.” Just as with writing automated tests, a team needs to be supported in the time it takes, and the process obstacles it faces, to create the LP design narratives. This comes from their team leads and line managers.
- “It’s harder to revise a design story (or tests).” System design is something that changes less often than much of the underlying code. And I would guess that if literate programming is used in the first place—forcing design to be considered and reviewed—then it’s more likely to be right first time, and less likely again to change.
- “What about boilerplate narrative (or tests)?” Yes, we do need to find the right way of integrating boilerplate code into our design documents. I’m not sure what the answer is there, but with some positive experimentation perhaps we will find it.
- “How does this work a world of objects, libraries, serverless and microservices?” And again, we need to find a way of writing effective design documents for larger, more complex and more distributed systems. We need to be able to embark on this without assuming we will get everything right first time.
To me it looks like the most effective way into the development process for literate programming is the code review step. One developer makes a pull request, and the responder reviews the change using the rendered (or raw) literate programming document. It should make their job so much easier, as well as ensuring the design is actually written down.
So we can deal with the objections (or at least those above). But the question remains: Should we?
Let’s not forget that our goal is to write reliable software that lasts. Our goal is not to use literate programming, or even to link design and code.
I do believe literate programming has a lot to offer us today. But we should launch the revolution with our eyes and ears open.