More on this book
Community
Kindle Notes & Highlights
Read between
April 20 - July 17, 2019
Functions should either do something or answer something, but not both.
It would appear that since the invention of the subroutine, innovations in software development have been an ongoing attempt to eliminate duplication from our source code.
The art of programming is, and has always been, the art of language design.
“Don’t comment bad code—rewrite it.” —Brian W. Kernighan and P. J. Plaugher1
comments are, at best, a necessary evil.
The proper use of comments is to compensate for our failure to express ourself in code.
Inaccurate comments are far worse than no comments at all.
Truth can only be found in one place: the code.
It takes only a few seconds of thought to explain most of your intent in code.
Keep in mind, however, that the only truly good comment is the comment you found a way not to write.
First of all, let’s be clear. Code formatting is important
The coding style and readability set precedents that continue to affect maintainability and extensibility long after the original code has been changed beyond recognition.
As in newspaper articles, we expect the most important concepts to come first, and we expect them to be expressed with the least amount of polluting detail. We expect the low-level details to come last. This allows us to skim source files, getting the gist from the first few functions, without having to immerse ourselves in the details.
Hiding implementation is not just a matter of putting a layer of functions between the variables. Hiding implementation is about abstractions!
A class does not simply push its variables out through getters and setters. Rather it exposes abstract interfaces that allow its users to manipulate the essence of the data, without having to know its implementation.
Objects hide their data behind abstractions and expose functions that operate on that data. Data structure expose their data and have no meaningful functions.
Mature programmers know that the idea that everything is an object is a myth. Sometimes you really do want simple data structures with procedures operating on them.
Error handling is important, but if it obscures logic, it’s wrong.
When you wrap a third-party API, you minimize your dependencies upon it: You can choose to move to a different library in the future without much penalty.
Test code is just as important as production code.
It is unit tests that keep our code flexible, maintainable, and reusable.
What makes a clean test? Three things. Readability, readability, and readability.
That is the nature of the dual standard. There are things that you might never do in a production environment that are perfectly fine in a test environment. Usually they involve issues of memory or CPU efficiency. But they never involve issues of cleanliness.
we want to test a single concept in each test function.
If you let the tests rot, then your code will rot too. Keep your tests clean.
If we cannot derive a concise name for a class, then it’s likely too large.
However, a system with many small classes has no more moving parts than a system with a few large classes.
When classes lose cohesion, split them!
Classes should be open for extension but closed for modification.
In an ideal system, we incorporate new features by extending the system, not by making modifications to existing code.
In essence, the DIP says that our classes should depend upon abstractions, not on concrete details.
If we are diligent about building well-formed and robust systems, we should never let little, convenient idioms lead to modularity breakdown.
It is a myth that we can get systems “right the first time.” Instead, we should implement only today’s stories, then refactor and expand the system to implement new stories tomorrow.
Software systems are unique compared to physical systems. Their architectures can grow incrementally, ifwe maintain the proper separation of concerns.
An optimal system architecture consists of modularized domains of concern, each of which is implemented with Plain Old Java (or other) Objects. The different domains are integrated together with minimally invasive Aspects or Aspect-like tools. This architecture can be test-driven, just like the code.
A premature decision is a decision made with suboptimal knowledge.
The agility provided by a POJO system with modularized concerns allows us to make optimal, just-in-time decisions, based on the most recent knowledge. The complexity of these decisions is also reduced.
A good DSL minimizes the “communication gap” between a domain concept and the code that implements it,
Domain-Specific Languages allow all levels of abstraction and all domains in the application to be expressed as POJOs, from high-level policy to low-level details.
Whether you are designing systems or individual modules, never forget to use the simplest thing that can possibly work.
According to Kent, a design is “simple” if it follows these rules: • Runs all the tests • Contains no duplication • Expresses the intent of the programmer • Minimizes the number of classes and methods The rules are given in order of importance.
Systems that aren’t testable aren’t verifiable. Arguably, a system that cannot be verified should never be deployed.
making sure our system is fully testable helps us create better designs.
Once we have tests, we are empowered to keep our code and classes clean. We do this by incrementally refactoring the code.
The fact that we have these tests eliminates the fear that cleaning up the code will break it!
The majority of the cost of a software project is in long-term maintenance.
code should clearly express the intent of its author.
By using the standard pattern names, such as COMMAND or VISITOR, in the names of the classes that implement those patterns, you can succinctly describe your design to other developers.
Someone reading our tests should be able to get a quick understanding of what a class is all about.
Care is a precious resource.

