- Avoid disinformation
- Make meaninful distinctions
- Imagine that you have a
Product
class. If you have another calledProductInfo
orProductData
, you have made the names different without making them mean anything different.Info
andData
are indistinct noise words as well. - Noise words are redundant. How is
NameString
better thanName
? Would aName
ever be a floating point number? Doing so would cause disinformation. - Imagine finding one class named
Customer
and another namedCustomerObject
- what should you understand as the distinction?
- Imagine that you have a
- Class names
- Classes and objects should have noun or noun phrase names like
Customer
,Wikipage
,Account
, andAddressParser
. Avoid words likeManager
,Processor
,Data
, orInfo
in the name of a class.
- Classes and objects should have noun or noun phrase names like
- Method names
- Methods should have verb or verb phrase names like
postPayment
,deletePage
, orsave
. Accessors, mutators, and predicates should be named for their value and prefixed withget
,set
, andis
according
- Methods should have verb or verb phrase names like
- Pick one word per concept
- Example: it's confusing to have
fetch
,get
, andretrieve
as equivalent methods of different classes
- Example: it's confusing to have
- Small!
- The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that.
- Do one thing
- Functions should do one thing. They should do it well. They should do it only.
- One way to know if a function is doing more than "one thing" is if you can extract another function from it with a name that is not merely a restatement of its implementation.
- Use descriptive names
- Choosing descriptive names will clarify the design of the module in your mind and help you improve it. It is not at all uncommon that hunting for a good name results in a favorable restructuring of your code.
- Function arguments
- Functions with many arguments are hard from a testing point of view. Imagine a function with multiple arguments and the difficulty of writing all the test cases to ensure that all the various combinations of arguments work properly.
- Note: The book suggests that no arguments are best - this is ideal in the case of OOP. A single argument is best in FP.
- Flag arguments
- Flag arguments are not ideal. Passing a boolean into a function complicates the signature of the method.
- Have no side effects
- A side effect is when your function promises to do one thing, but it also does other hidden things.
- Error handling is one thing
- Functions should do one thing - error handling is one thing. A function that handles an error should do nothing else.
- Misc.
- Functions are the verbs of the language, classes are the nouns.
- The art of programming has been the art of language design.
- Master programmers think of systems as stories to be told rather than programs to be written.
- Comments do not make up for bad code
- One of the more common motivations for writing comments is bad code. We write a module and we know it is confusing and disorganized. So we write comments when we should be refactoring our code.
- Explain yourself in code
- Use your code as a vehicle for explanation, rather than comments.
- Which would you rather see?
if (employee.flags && HOURLY_FLAG) && employee.age > 65
orif employee.isEligibleForFullBenefits()
- Explanation of intent
- Sometimes a comment goes beyond just useful information about the implementation; it provides the intent behind the decision.
- Redundant comments
- When a comment simply describes the code itself, it is useless, i.e.
i++ // increment i
- When a comment simply describes the code itself, it is useless, i.e.
- Write your Try-Catch statement first
- Starting with your Try-Catch statement helps you define what the user of the code should expect, no matter what goes wrong with the code executed in the try.
- Provide context with exceptions
- Create informative error messages and pass them along with your exceptions
- Don't return
null
- Returning
null
creates extra work for ourselves as we have continually make sure we check against it - Instead return a
SPECIAL CASE
object that returns a default behavior or wrap it in a method that throws an exception - https://martinfowler.com/eaaCatalog/specialCase.html
- Returning
- Don't pass
null
- Exception is when an API is expecting you to pass
null
- Exception is when an API is expecting you to pass
- The three laws of TDD
-
- You may not write production code until you've written a failing test
-
- You may not write more of a unit test than is sufficient to fail, and not compiling is failing
-
- You may not write more production code than is sufficient to pass the currently failing test
- Addendum: ask yourself; What is the least amount of code I can write to pass this test?
-
- Dirty tests
- Test code is just as important as production code
- Keep tests clean and up to date with evolving production code
- Tests enable the -ilities
- Tests keep code flexible, maintainable, and reusable
- If you have tests, you do not fear making changes to the code!
- Classes should be small!
- If we cannot derive a concise name for the class, then it's likely too large
- The more ambiguous the class name, the more likely it has too many responsibilites
Processor
,Manager
, orSuper
often hint at unfortunate aggregation of responsibilites
- Single Responsibility Principle (SRP)
- SRP states that a class or module should have one, and only one, reason to change. Classes should have one responsibility.
The problem is too many of us think we are done once the program works. We fail to switch to the other concern of organization and cleanliness. We move on to the next problem rather than going back and breaking the overstuffed classes into decoupled units with single responsibilities.
At the same time, many developers fear that a large number of small, single-purpose classes makes it more difficult to understand the bigger picture. They are concerned that they must navigate from class to class in order to figure out how a large piece of the work gets accomplished.
However, a system with many small classes has no more moving parts than a system with a few large classes. Do you want your tools organized 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?
- Scaling up
- 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. This is the essence of iterative and incremental agility.
- Software systems are unique compared to physical systems. Their architectures can grow incrementally, if we maintain the proper separation of concerns.
- Test drive the system architecture
- It is not necessary to do a Big Design Up Front (BDUF) - it is harmful because it inhibits adapting to change, due to the psychological resistance to discarding prior effort and because of the way architecture choices influence subsequent thinking about the design.
- We can start a software project with a naively simple but nicely decoupled architecture, adding more infrastructure as we scale up
- We do not go in rudderless, we have some expecations of the general scope, goals, and schedule for the project as well as the general structure of the resulting system
- Misc.
- Whether you are designing systems or individual modules, never forget to use the simplest thing that can possibly work
- Expressive
- You can express yourself by choosing good names. We want to be able to hear a class or function name and not be surprised when we discover its responsibilities.
- You can also express yourself by keeping your functions and classes small. Small classes and functions are usually easy to name, easy to write, and easy to understand.
- The most important way to be expressive is to try. Care is a precious resource.
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. They may fear that they don't have time to improve the structure and design of their code, but I disagree. Nothing has a more profound and long-term degrading effect upon a development project than bad code. Bad schedules can be redone, bad requirements can be redefined, bad team dynamics can be repaired, but bad code rots and becomes a weight that drags the team down. Cleaning up bad code is expensive.
- Replace magic numbers with constants
- i.e. 86,400 should be hidden behind a SECONDS_PER_DAY constant. If you're printing 55 lines per page, hide the number behind a LINES_PER_PAGE constant.
- Encapsulate conditionals
- Extract functions that explain the intent of the the conditional
- i.e.
if shouldBeDeleted(timer)
vsif timer.hasExpired() && !timer.isRecurrent()
- Note: These notes are light because this chapter was mostly a recap of everything in the other chapters.
Nice, thanks!