Thursday, July 1, 2021

Clean Code

This is a summary of my thoughts on Clean Code divided up into chapters as we work our way through the book in book club.

Chapter 1

  • Huge code bases need clean code, liked the story about an app Bob used and it got worse and worse as the years went on
  • Development teams eventually mutiny against management when code base is gross, but then you have this situation where old legacy code is maintained while new code is written, features need to be parallel, this takes a long time
  • I like this quote a lot from Ward Cunningham

You can call it beautiful code when the code also makes it look like the language was made for the problem

  • The boy scouts rule

Leave the campground cleaner than you found it

  • This is why we need code reviews and tests and things that might seem to be slowing development down
  • Learning from past mistakes

Chapter 2

  • One letter variable names, also don't like the T in TypeScript generics
  • I think learning good variable naming is even more important in dynamic languages, ruby sets some good standards

Chapter 3

If you use exceptions instead of returned error codes, then the error processing code can be separated from the happy path code and can be simplified

  • In Go hard to handle errors like this, see lots of if err != nil which is kind of like error codes

Only be one return statement in a function, no break or continue statements in a loop

  • Really hard to do!

So if you keep your functions small, then the occasional multiple return, break, or continue statement does no harm and can sometimes even be more expressive than the single entry single exit rule

Chapter 4

  • Journal comments, interesting that this was something that was done before source control, would have taken up a lot of time

So commented-out code gathers like dregs at the bottom of a bad bottle of wine

  • Author is flaming javadoc comments
  • Code generation changes this idea of file sizes, you don't really know what's being generated

Chapter 5

We would like a source file to be like a newspaper article

If the newspaper were just one long story containing a disorganised agglomeration of facts, dates, and names, then we simply would not read it

  • Blank lines are super important and effect readability
  • Wasting time on the where, not what when reading source code

This is frustrating because you are trying to understand what the system does, but you are spending your time and mental energy on trying to locate and remember where the pieces are

  • Nice little section on classes and instance variables

The important thing is for the instance variables to be declared in one well-known place

Everybody should know where to go to see the declarations

  • The caller should be above the callee in dependent functions
  • Strive to keep line lengths short, beyond 120 characters is careless

Chapter 6

  • Didn't know that Active Record had this specific meaning, data transfer object

They often become the first in a series of translation stages that convert raw data in a database into objects in the application code

Chapter 7

  • Also agree with this

So, the things that are hard for OO are easy for procedures, and the things that are hard for procedures are easy for OO!

  • Error handling can be tedious

Error handling is just one of those things that we all have to do when we program

  • Nods to this

Many code bases are completely dominated by error handling. When I say dominated, I don’t mean that error handling is all that they do. I mean that it is nearly impossible to see what the code does because of all of the scattered error handling. Error handling is important, but if it obscures logic, it’s wrong.

If you work in a code base with code like this, it might not look all that bad to you, but it is bad! When we return null, we are essentially creating work for ourselves and foisting problems upon our callers.

Chapter 8

  • Java based solution, returning empty list example
  • This is an interesting way to test out 3rd party code

Instead of experimenting and trying out the new stuff in our production code, we could write some tests to explore our understanding of the third-party code.

Not only are learning tests free, they have a positive return on investment. When there are new releases of the third-party package, we run the learning tests to see whether there are behavioural differences.

Chapter 9

  • Testing chapter, Uncle Bob loves his testing
  • No production code until write failing test, not sure if this is so useful in practise

The sheer bulk of those tests, which can rival the size of the production code itself, can present a daunting management problem

What this team did not realise was that having dirty tests is equivalent to, if not worse than, having no tests

What makes tests readable? The same thing that makes all code readable: clarity, simplicity, and density of expression.

  • Creating domain specific language for chunks of your testing, example would be kind of like what I did for DismissJobButton
  • The heating example was kind of interesting, individual letters represented toggle states

minimise the number of asserts per concept and test just one concept per test function

  • So I think having multiple asserts is okay sometimes so long as the concept is the same

and on your laptop while riding home on the train without a network

  • Not sure if this is possible?

Unit tests should be written just before the production code that makes them pass. If you write tests after the production code, then you may find the production code to be hard to test. You may decide that some production code is too hard to test. You may not design the production code to be testable.

  • Again this is something that I can't fully get on board with

Chapter 10

  • This is going to be all in on OOP and Java best practises

If we cannot derive a concise name for a class, then it’s likely too large

For example, class names including weasel words like Processor or Manager or Super often hint at unfortunate aggregation of responsibilities

Getting software to work and making software clean are two very different activities. Most of us have limited room in our heads, so we focus on getting our code to work more than organisation and cleanliness.

The problem is that too many of us think that we are done once the program works

  • This is all very true, holds up well

Do you want your tools organised into toolboxes with many small drawers each containing well-defined and well-labeled components? Or do you want a few drawers that you just toss everything into?

  • Great metaphor

If we promoted those four variables to instance variables of the class, then we could extract the code without passing any variables at all.

  • This is something I slowly learnt when creating ruby classes

Classes should be open for extension but closed for modification.

Chapter 11

This means we can start a software project with a "naively simple" but nicely decoupled architecture, delivering working user stories quickly, then adding more infrastructure as we scale up. Some of the world’s largest Web sites have achieved very high availability and performance, using sophisticated data caching, security, virtualisation, and so forth, all done efficiently and flexibly because the minimally coupled designs are appropriately simple at each level of abstraction and scope.

We all know it is best to give responsibilities to the most qualified persons. We often forget that it is also best to postpone decisions until the last possible moment. This isn’t lazy or irresponsible; it lets us make informed choices with the best possible information.

  • When talking about using standards

Building construction is a marvel to watch because of the pace at which new buildings are built (even in the dead of winter) and because of the extraordinary designs that are possible with today’s technology. Construction is a mature industry with highly optimised parts, methods, and standards that have evolved under pressure for centuries.

  • Talked about DSLs, agree with him

Chapter 12

  • Yes to all these things
  • Kind of a summary of many pieces from previous chapters

The majority of the cost of a software project is in long-term maintenance. In order to minimise the potential for defects as we introduce change, it’s critical for us to be able to understand what a system does. As systems become more complex, they take more and more time for a developer to understand, and there is an ever greater opportunity for a misunderstanding. Therefore, code should clearly express the intent of its author. The clearer the author can make the code, the less time others will have to spend understanding it. This will reduce defects and shrink the cost of maintenance.

Chapter 13

  • Concurrency is hard
  • A classic example of why concurrency

Or consider a system that interprets large data sets but can only give a complete solution after processing all of them. Perhaps each data set could be processed on a different computer, so that many data sets are being processed in parallel.

Concurrency can sometimes improve performance, but only when there is a lot of wait time that can be shared between multiple threads or multiple processors.

  • The below quote is very true, async in React

Concurrency bugs aren’t usually repeatable, so they are often ignored as one-offs instead of the true defects they are.

  • Can you do this in React?

Recommendation: Keep your concurrency-related code separate from other code

  • This sounds like the JavaScript event loop

This means producers must wait for free space in the queue before writing and consumers must wait until there is something in the queue to consume

  • Philosophers and spaghetti metaphor

Replace philosophers with threads and forks with resources and this problem is similar to many enterprise applications in which processes compete for resources. Unless carefully designed, systems that compete in this way can experience deadlock, livelock, throughput, and efficiency degradation.

  • More good advice

Write tests that have the potential to expose problems and then run them frequently, with different programatic configurations and system configurations and load. If tests ever fail, track down the failure. Don’t ignore a failure just because the tests pass on a subsequent run.

Recommendation: Do not ignore system failures as one-offs.

  • Interesting that multithreaded code behaves differently depending on platform

Chapter 14

  • Nice piece of Java followed by bad piece of Java
  • Kind of hard to critique this apart from basic programming knowledge I have, what classes should look like

If we have learned anything over the last couple of decades, it is that programming is a craft more than it is a science. To write clean code, you must first write dirty code and then clean it.

We learned this truth in grade school when our teachers tried (usually in vain) to get us to write rough drafts of our compositions

Although you can find plenty to complain about in this code, it’s really not that bad. It’s compact and simple and easy to understand. However, within this code it is easy to see the seeds of the later festering pile. It’s quite clear how this grew into the latter mess.

The addition of just two more argument types had a massively negative impact on the code. It converted it from something that would have been reasonably maintainable into something that I would expect to become riddled with bugs and warts.

  • Incrementally changing the code with TDD

It is not enough for code to work. Code that works is often badly broken. Programmers who satisfy themselves with merely working code are behaving unprofessionally

  • Find this kind of harsh 🤷‍♂️

Chapter 15

  • Looking at JUnit, testing framework