Yes, regular readers of this blog1, it has been nine days since I last looked at this book (and twenty days since the chapter one post).
Good thing I wrote about the previous chapter as I’d already forgotten half of the information in it and had to re-read my first post on it.
Actually, if you needed a refresher on that, why don’t you just click here and go read it yourself? Or maybe use the little breadcrumb trail thing at the bottom of this page that I took from my Arkham Horror playthrough.
Hopefully you won’t get transported to a run-through of me losing at that game while you read my thoughts on this book due to any copy/paste errors.
We continue our journey into Attempting-To-Remain-Employed-During-An-Economic-Downturn™ with chapter two: A Pragmatic Approach.
A Pragmatic Approach
There are a bunch of tips and tricks in software that are so obviously the right way to do things that no one actually documents them2.
I also learned what axiomatic means, so I can sound utterly pretentious when I drop that into conversation in the future.
The Essence of Good Design
Things are well designed if they can adapt to the people around them3, and in software that means change.
We should strive to Single Responsibility, to decouple our code, to name things well. All of these makes it easier to change the code.
Whenever we write code we should make it replaceable so that our code doesn’t become a road-block to progress.
DRY - The Evils of Duplication
“Don’t Repeat Yourself” means that we should only write a piece of code to do a thing in one place, and not in multiple places throughout the codebase.
This will ensure that any changes only need to be made in one place, but also makes the system easier to understand in the first place.
This is one of those axiomatic things, you know. I have recently had the misfortune to work with geospatial mapping applications using JavaScript, and the code was a real mess of spaghetti. There were multiple ways to render the controls, to display various legends on the map, to load in the data etc.
Each map was in its own JavaScript file and contained all the same methods that that other 35 files had, so if you wanted to bolt an extra layer onto a map, well, that’s going to take 34 times more effort. Awful
Of course, DRY doesn’t just mean code, it means knowledge throughout the system as a whole. The example of interacting via an interface is used.
In order to do anything useful your code will need to interact with someone outside of itself, like making an API call. Well, in order to do that, your code needs to know what the interface for that API is, so that knowledge is duplicated. The API itself and your code. Even worse, if some idiot companies do not bother versioning their APIs then any change they make will break your code. This is what the book call Representational Duplication.
Also, just because two functions may do the same thing, they do not necessarily mean the same thing. The example in this chapter is a function to validate a quantity, and a function to validate the age of something. They both check if the parameter is an integer, and whether it is greater than or equal to the minimum allowed integer. However one is for age and one is for quantity. They mean different things4.
We should also stop commenting every single thing and just write simple functions that explain what is happening. I tend to do this with every thing I write. Mainly as I’ve had to maintain some truly garbage code. Show-off contractors who wrote the most unintuitive cursed code you have seen, all on one line, all with abstract parameters names5
Another good point this chapter makes is to use properties (or accessor functions as they call them) for getting and setting data in a class.
I thought this was an axiom so it’s good to see it written down.
Orthogonality
Appropriately this sections opens with a description of what orthogonality is, because I had no idea, and I am pretty sure most people don’t either.
It means decoupling stuff to be independent. I am not sure why we can just use the word “decoupled” or “independent” but there you go.
We’re orthogonal and you will be too.
We should use encapsulation (although it doesn’t explicitly say that) rather than changing an object’s state externally, and various other tips on writing code that doesn’t depend on external factors.
After reading this section is doesn’t seem to say anything new compared to the DRY section.
Reversibility
This section is just saying that we should always strive to write flexible components, and not tie ourselves down to a particular vendor or technology that may be different in the future.
For example, maybe we don’t want to hard-wire everything into using a SQL database with Entity Framework throughout. It would be better to write a database interaction component that we can swap out later on if required.
This extends to the architecture and infrastructure too. There is a lot of architectural volatility. The best practices keep changing, we’ve gone from in-house servers, to cloud VMs, to containerised version of that, to serverless, and now back to on-prem servers.
The best way to deal with all this change is to write components and keep implementations abstracted away.
Tracer Bullets
This is another word for immediate feedback in a world of changing varibles. We might be working with vague requirements (unheard of!), or using languages and frameworks we’ve not had the experience of before. Rather than defining the whole system up front, the better option is to try to find something that can go from requirement to visible output quickly and repeatably.
We should look at areas that have the biggest amount of doubt surrounding them, or that are the most important to the system, or the riskiest areas, and start work there.
If there are various layers to the system, like a UI talking to an API via authentication, then moving on the the business logic, mapping to data models, and accessing a database, then we should write all the bits that allow that journey to happen. This code will not be disposable, it is going to be actual production code. It is just not fully functional yet, but you can then adjust the code to meet further requirements.
The benefits of this approach include:
- Users seeing something working
- Developers not longer have a blank sheet of paper intimidating them
- You have an integration platform to add more things to
- You have something to demo to the stakeholders
- You can measure progress more easily
This “tracer bullet” code doesn’t necessarily meet the requirements, or is too slow, or doesn’t have access to all the necessary data, but that is where you can adjust what has been written.
Tracer bullets can often be confused with vertical slicing and prototyping. Tracer bullet code is somewhere in the middle of these two. Vertical slicing require more upfront building of the services that are being sliced, and prototyping should involve throwing the code prototyped code away after having learned some lessons from it6.
If you know that you work in an organisation that will want to use the prototype as production code, then you are better off not bothering with prototyping at all, and stick to tracer bullet or vertical slicing instead7.
Domain Languages
The language chosen tends to shape the final product, and as languages are just another tool, we should choose the right one for the right problem.
When writing tests there are languages like Cucumber that can be read by the customers of the software. The tests are written in a way that looks kind of like a normal language “Given X When Y Then Z”. However this still gets compiled down into a different language by a developer, and how often does a customer look at tests anyway? They don’t even know what they want, so how can they decide whether the tests seem correct? Answer: they cannot.
It is probably best to just write the code in the language of the domain rather than something else that requires parsing.
Estimating
The final section of chapter two doesn’t really say anything useful. It all sounds like common sense to me, such as using different estimating units (days, weeks, months) depending on how big the estimate is.
But the general advice is to just get better at estimating through experience.
I have always been good at estimating. I say a large number which takes into account all of the inefficiencies and blockers that are normally thrown in my way by the company that are never acknowledged or resolved. This number is so large that I am told to re-estimate the work while pretending that the difficulties do not exist. That value then becomes the fake number that we use for the story. The story then runs over due to the unforseen difficulties. After several cycles of this way of working I then leave to get a new job.
« Chapter 1: A Pragmatic Philosophy | Chapter 2: A Pragmatic Approach | » Chapter 3: The Basic Tools (coming soon?)
21st October 2024
-
Myself ↩︎
-
Apart from the thousands of books on each of them, I suppose ↩︎
-
No, things are well designed if they are good at the the thing they are built to do. I don’t expect to be able to put pictures up on the wall with my hacksaw ↩︎
-
Though, to be fair, I would likely have just used one function here anyway. YAGNI! ↩︎
-
Ah yes the trio of terrible names, “x”, “y”, and “z” ↩︎
-
I don’t think I’ve ever worked anywhere that throws the code away. Usually it forms the bedrock of the critical component ↩︎
-
Valuable advice that would have saved me lots of aggravation in the past ↩︎