If you stay in the same company, and work in the same code base for years, you may become a specialised authority, but will that help you if you decide to work somewhere else? You can learn faster by moving from code base to code base. Try some back-end development. Do some front-end development. Perhaps try some game programming, or some machine learning. This will expose you to a wide range of problems that will accumulate as experience. This is strikingly similar to the old European tradition of journeyman years. A craftsman like a carpenter or roofer would travel around Europe, working for a while in a place before moving on to the next. Doing so exposed them to alternative solutions to problems. It made them better at their craft.
I saw skill as nothing but accumulated experience. It seemed to me that there was no methodology to software development. That everything depended on circumstances. That there was no right or wrong way to do things. That programming was basically an art.
A fundamental problem with software development is that there’s a lot going on. Our brains aren’t good at keeping track of many things at the same time. We also have a tendency to skip doing things that don’t seem important right now. The problem isn’t that you don’t know how to do a thing; it’s that you forget to do it, even though you know that you ought to. This problem isn’t isolated to programming. Pilots suffer from it, and they invented a simple solution to the problem: checklists.
A checklist isn’t a complex flowchart with detailed instructions. It’s a simple list of items that you can cover in a few minutes.
A baseball bat and ball costs $1.10. The bat costs a dollar more than the ball. How much does the ball cost?
“This is what happens with poor internal quality. Progress is rapid initially, but as time goes on it gets harder to add new features. Even small changes require programmers to understand large areas of code, code that’s difficult to understand. When they make changes, unexpected breakages occur, leading to long test times and defects that need to be fixed.”
form a hypothesis about the impact of the change you’re about to make make the change measure the impact and compare it to your prediction
some code produces no immediately measurable value. You might, on the other hand, be able to measure the absence of it. A straightforward example is security. You may not be able to measure the value of adding authentication to an online system, but you can probably measure the absence of it.
You spend more time reading code than writing it.
“Even small changes require programmers to understand large areas of code, code that’s difficult to understand.”
Place related code close together. All the dependencies, variables, and decisions required should be visible at the same time.
Can computer science help? I don’t see why not, but computer science isn’t (software) engineering, just like physics isn’t the same as mechanical engineering. Such disciplines can interact, but they aren’t the same. Successful practices can provide inspiration and insight for scientists, and results from science can be applied to engineering, as suggested by figure 3.3. Figure 3.3 Science and engineering interact, but aren’t the same.
As Martin Fowler put it: “Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” [34]
Throughout this book, I’ll use the number seven as a proxy for the limits of human short-term memory. Humane code, then, implies fewer than seven dependencies, that cyclomatic complexity is at most seven, and so on.
I use the term complexity in the same way that Rich Hickey uses it [45]: as an antonym to simplicity. Complex means ‘assembled from parts’, as opposed to simple, which implies unity.
Software engineering should be the deliberate process of preventing complexity from growing. Perhaps you recoil from all of this. You may think that it’s going to slow you down. Yes, that’s the point. To paraphrase J.B. Rainsberger [86], you probably need slowing down. The faster you type, the more code you make that everyone has to maintain. Code isn’t an asset; it’s a liability [77].
Sometimes, the best strategy is to just get started. You should still think and plan ahead. There’s no reason to be wilfully nonchalant or blasé about thinking ahead, but just as too little planning can be bad for you, so can too much. If you’ve already established your deployment pipeline [49], the sooner you can deploy a piece of working software, no matter how trivial, the sooner you can start to collect feedback from stakeholders
The idea behind vertical slicing is to get to working software as soon as possible. You do that by implementing the simplest feature you can think of—all the way from user interface to data store.
I’ve met programmers who spent months perfecting a home-grown data-access framework before even trying to use it to implement a feature. They often learn that they’ve made assumptions about usage patterns that don’t fit reality. You should avoid Speculative Generality [34], the tendency to add features to code because you ‘might need it later’. Instead, implement features with the simplest possible code, but look out for duplication as you add more.
layers are typically illustrated as horizontal strata, with data arriving on top and being persisted at the bottom. In order to implement a complete feature, you’ll have to move data all the way from entry point to persistence layer, or the other way. When layers are horizontal shapes, then a single feature is a vertical slice through all of them.
Notice that the assertion only considers the most superficial property of the system: does it respond with an HTTP status code in the 200 range (e.g. 200 OK or 201 Created)? I decided to refrain from verifying anything stronger than that, because the current behaviour (it returns Hello World!)only acts as a placeholder. It should change in the future.