Unofficial List of Errata for Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin
- Page 7, first paragraph: "...use of a myriad little..." should be "...use of myriad little...". (Lars Cremean)
- Page 21, 5th paragraph: "we've changed the names..." should be "We've changed the names...". (Felix Siegrist)
- Page 22, 2nd code block: "private Date modificationTimestamp;;" should be "private Date modificationTimestamp;". (Alessandro Aime)
- Page 23, Listing: "(realdays / WORK_DAYS_PER_WEEK)" should be "(realTaskDays / WORK_DAYS_PER_WEEK)". (Felix Siegrist)
- Page 24, 2nd code block: "private String m_desc;" is refactored to "String description;". The accessibility level of this member variable increases for no reason. (Daniel Baechli)
- Page 30, 2nd paragraph: "MAC addresses, port addresses, and Web addresses" should instead be "postal addresses, MAC addresses, and Web addresses" to match the succeeding names.
- Page 32, footer: URL "www.fitnesse.org" missing an 's'.
- Page 33, Listing 3-2: The refactored code is not semantically equivalent to the original code, for the code may behave differently depending on the implementation of PageData. (Daniel Baechli)
- Page 39, Listing 3-5: "SalariedEmployee" missing an 'e'.
- Page 41, 5th paragraph: "transform-(StringBuffer out)" should be "transform(StringBuffer out)".
- Page 42, 2nd line: "writeField(output-Stream, name)" should be "writeField(outputStream, name)". (Felix Siegrist)
- Page 42, 2nd paragraph: "output-Stream" should be "outputStream". (Felix Siegrist)
- Page 54, paragraph under code fragment: "it's" should be "its". (Daniel Wagner-Hall)
- Page 60, 2nd paragraph: In three instances, "loadProperties.load" should be "loadedProperties.load". (Lars Cremean)
- Page 63, Listing 4-3: The field is called "duration", but the parameter is "durationInMinutes". They should be the same, or at least fix the assignment to be "this.duration = durationInMinutes;". (Alexander Momchilov)
- Page 73, Listing 4-8: All of the methods reference instance variables or call methods that reference instance variables, but are erroneously marked static. (Alexander Momchilov)
- Page 74, 2nd paragraph: "The second argument is..." should be "The second comment is...". (Felix Siegrist)
- Page 78, Listing 5-1: The StringBuffer is used entirely locally, so there's no need for its synchronization. Should use StringBuilder instead. (Alexander Momchilov)
- Page 86, 2nd code block: The method name "determinant" is inaccurate. Quadratic equations have discriminants, while matrices have determinants. (Connor Glosser)
- Page 88, 2nd paragraph, 2nd line: "hiearchy" should be "hierarchy". (Felix Siegrist)
- Page 95, 2nd paragraph, 3rd sentence: "data structure" should be "data structures". (Dennis Pagano)
- Page 95, 2nd paragraph: "complimentary" should be "complementary".
- Page 99, 2nd code block: "ctx.getScratchDirectoryOption(..." should be "ctxt.getScratchDirectoryOption(...". (Lars Cremean)
- Page 110, 2nd code block: "peristentStore" should be "persistentStore". (Francis King)
- Page 111, first line after first code block: "If we change getEmployee..." should be "If we change getEmployees...". (Felix Siegrist)
- Page 115, upper half: "Map<Sensor>" should be replaced three times by "Map<String, Sensor>". This assumes that sensorId is of type String as in the code block in middle of page. (Felix Siegrist)
- Page 120, 2nd paragraph: "lest it end up" should be "lest it ends up". (Henrik Warne)
- Page 128, Listing 9-3: "Threashold" should be "Threshold". (Aaron Fleming)
- Page 128-130, Listing 9-5: Not technically an error, but this code is crazy, and a poor supporting example of a DSL. (Alexander Momchilov)
- Page 138, Listing 10-1, first line: "setAçowDragging" should be "setAllowDragging". (Felix Siegrist)
- Page 138, 2nd to last line: "a guidelines for" should be "a guideline for". (Felix Siegrist)
- Page 139, 3rd paragraph: "one of the more important concept in" should be "one of the more important concepts in". (Felix Siegrist)
- Page 146, 4th paragraph: "a list prime numbers" should be "a list of prime numbers". (Felix Siegrist)
- Page 160, footer 7: "[AspectJ]]" should be "[AspectJ]". (Felix Siegrist)
- Page 161, Listing 11-3: "import java.utils.*" should be "import java.util.*" for Bank.java and BankImpl.java. (Zefi)
- Page 162, Listing 11-3: The constructor name does not match the class name. (Daniel Baechli)
- Page 187, first full paragraph: "...systems can be frustratingly." should be "...systems can be frustrating.". (Lars Cremean)
- Page 187, "trial an error" should be "trial and error".
- Page 188, footer 16: "it not guaranteed" should be "it is not guaranteed". (Felix Siegrist)
- Page 190, code block: "ThreadJiglePoint" should be replaced three times by "ThreadJigglePoint". (Felix Siegrist)
- Page 194, last line of paragraph after Listing 14-1: "command-line argument" should be "command-line arguments". (Felix Siegrist)
- Page 213, 3rd paragraph and Listing 14-11 title (two instances): "ArgumentMarshaller" should be "ArgumentMarshaler". (Lars Cremean)
- Page 213, Listing 14-11: This code has a syntax error because the number of opening and closing curly braces do not match. (Daniel Baechli)
- Page 214, 3rd and 4th paragraphs (three instances total): "...Marshaller" should be "...Marshaler". (Lars Cremean)
- Page 216: The line "intArgs.get(argChar).setInteger(Integer.parseInt(parameter));" should be formatted as "intArgs.get(argChar).setInteger(Integer.parseInt(parameter));" (as "new Integer(parameter)" is additionally replaced with "Integer.parseInt(parameter)"). (Daniel Baechli)
- Page 217, first paragraph: For consistency, "With all the marshalling..." should be "With all the marshaling..."; and last paragraph (three instances): "...Marshaller" should be "...Marshaler". (Lars Cremean)
- Page 217: "The tests all still passed." The tests cannot be run because the code does not compile, since StringArgumentMarshaler and IntegerArgumentMarshaler have no implementation of the set method. (Daniel Baechli)
- Page 218: "This compiled and obviously failed the tests." The code does not compile because StringArgumentMarshaler and IntegerArgumentMarshaler have no implementation of the set method. (Daniel Baechli)
- Page 220, first paragraph: For consistency, in two instances, "integers" should be "Integers". (Lars Cremean)
- Page 221: "Of course, the tests continued to pass." However, the tests do not run, because the code does not compile. The set implementation in IntegerArgumentMarshaler throws a checked exception, but the abstract set method declaration in ArgumentMarshaler does not, which causes a compilation error. Therefore, the abstract set method declaration in ArgumentMarshaler must throw ArgsException. (Daniel Baechli)
- Page 224: The line "Args.ArgumentMarshaler am = marshalers.get(arg);" should be formatted as "Args.ArgumentMarshaler am = marshalers.get(arg);".
- Page 226, Listing 14-12: The code in the listing should be the same as the refined code but has several slight differences. (Daniel Baechli)
- Page 231, 3rd paragraph: "currentArg" should be replaced twice with currentArgument" and "BooleanArgumentMarshaler" should be "IntegerArgumentMarshaler". (Felix Siegrist)
- Page 233, last paragraph: "iterator" should be "Iterator", and "ArgumentMarshaller" should be "ArgumentMarshaler". (Lars Cremean)
- Page 233: The line "private void setBooleanArg(ArgumentMarshaler m, Iterator<String> currentArgument) throws ArgsException {" should be formatted as "private void setBooleanArg(ArgumentMarshaler m, Iterator<String> currentArgument) throws ArgsException {". (Daniel Baechli)
- Page 236, text: "Touche!" should be "Touché!". (Lars Cremean)
- Page 237: "We can also turn ArgumentMarshaler into an interface." It goes unmentioned that "extends ArgumentMarshaler" for the classes BooleanArgumentMarshaler, StringArgumentMarshaler and IntegerArgumentMarshaler must be "implements ArgumentMarshaler" for the change from class to an interface. Otherwise, the code does not compile. (Daniel Baechli)
- Page 237: For the deletion of the set(String) method in ArgumentMarshaler, deletion should also occur in BooleanArgumentMarshaler, StringArgumentMarshaler and IntegerArgumentMarshaler. (Daniel Baechli)
- Page 240: The line "private boolean parse() throws ArgsException {" should be formatted as "private boolean parse() throws ArgsException {". (Daniel Baechli)
- Page 240: The line "private void parseElement(char argChar) throws ArgsException {" should be formatted as "private void parseElement(char argChar) throws ArgsException {". (Daniel Baechli)
- Page 241: The line "public void set(Iterator<String> currentArgument) throws ArgsException {" should be formatted as "public void set(Iterator<String> currentArgument) throws ArgsException {". (Daniel Baechli)
- Page 241: The line "errorCode = ArgsException.ErrorCode.MISSING_INTEGER;" should be formatted as "errorCode = ArgsException.ErrorCode.MISSING_INTEGER;". (Daniel Baechli)
- Page 241: First "throw new ArgsException();" line in IntegerArgumentMarshaler should be formatted as "throw new ArgsException();". (Daniel Baechli)
- Page 241: Second "throw new ArgsException();" line in IntegerArgumentMarshaler should be formatted as "throw new ArgsException();" (Daniel Baechli)
- Page 245, Listing 14-15: This should be the code resulting from the refinement process. But the listed code is the result of the refinement process and further refinements. This fact is nowhere stated. (Daniel Baechli)
- Page 247, Listing 14-16: This should be the code resulting from the refinement process. But the listed code is the result of the refinement process and further refinements. This fact is nowhere stated. (Daniel Baechli)
- Page 256, Listing 15-3 title: "ComparisonCompator.java" should be "ComparisonCompactor.java". (Felix Siegrist)
- Page 257, Listing 15-3 title: "ComparisonCompator.java" should be "ComparisonCompactor.java". (Felix Siegrist)
- Page 262, 2nd paragraph, after code block: "...it didn't use to..." should be "...it didn't used to...". (Lars Cremean)
- Page 263, Listing 15-5: in formatCompactedComparison() there is still a temporal coupling between findCommonPrefixAndSuffix() and the following calls to compact(). (Felix Siegrist)
- Page 268, 5th paragraph: "MonthCodeToQuarter (line 334)" should be "monthCodeToQuarter (line 356)". (Felix Siegrist, Daniel Baechli)
- Page 269, first line: "testWeekdayCodeToString" should be "stringToWeekdayCode". (Felix Siegrist)
- Page 270, 2nd to last line: "world" should be "word". (Felix Siegrist)
- Page 272, first paragraph, first sentence: "it's" should be "its". (Tod Gentille)
- Page 275, next to last paragraph: "LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_MONTH" should be "LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH". (Lucas Arruda)
- Page 276, 2nd paragraph: "(lines 18-205)" should be "(lines 189-205)". (Felix Siegrist)
- Page 277, code block, end of 4th line: A stray 's' should be deleted. (Lars Cremean)
- Page 279, 3rd code block: The code "month.lastDay()" is invalid, as the method "lastDay()" has not been introduced at this point. The method is not introduced along with the refactoring and only occurs in the final code listing. (Daniel Baechli)
- Page 279, last paragraph: "getYYY" should be "getYYYY". (Henrik Warne)
- Page 280: The two occurrences of "getDayOfWeek().index" should be "getDayOfWeek()". The method signature of "getDayOfWeek()" is still "public int getDayOfWeek()" at this point. The referenced method "public Day getDayOfWeek()" is only introduced on page 282. (Daniel Baechli)
- Page 280, last sentence: "The exact same analysis" should be "The same analysis" or at least "Exactly the same analysis". "Exact same" is ungrammatical. (Tod Gentille)
- Page 281: "getDayOfWeek().index" should be "getDayOfWeek()". The method signature of "getDayOfWeek()" is still "public int getDayOfWeek()" at this point. The referenced method "public Day getDayOfWeek()" is only introduced on page 282. (Daniel Baechli)
- Page 282, first paragraph: "...(lines 838-844)." should be "...(lines 829-836).". (Lars Cremean)
- Page 282, 3rd paragraph: "The getYYYY, getMonth, and getDayOfMonth methods are nicely abstract." should be "The getYear, getMonth, and getDayOfMonth methods are nicely abstract.". The method "getYYYY" was renamed to "getYear" on page 279. (Daniel Baechli)
- Page 282, 3rd paragraph: "SpreadSheetDate" should be "SpreadsheetDate". (Lars Cremean)
- Page 283, 2nd to last pagraph: "making the all three methods" should be "making all three methods". (Felix Siegrist)
- Page 289, G4, first paragraph: "it's first" should be "its first". (Joe Bowbeer)
- Page 290, 4th paragraph: "...database schemae." should be "...database schemas.". (Lars Cremean)
- Page 296, first paragraph, 2nd to last sentence: "it's" should be "its". (Tod Gentille)
- Page 296, G18, 3rd paragraph: "it's" should be "its". (Joe Bowbeer)
- Page 301, 2nd paragraph: "when a List will due" should be "when a List will do". (Henrik Warne)
- Page 309, in the HourlyPayGrade enum: "LEUTENANT_JOURNEYMAN" should be "LIEUTENANT_JOURNEYMAN". (Alex Moore)
- Page 312, N5: The code example wants to illustrate that short variable names (like "i") are appropriate for short scopes. But it also uses a variable named "g", which is not an appropriate name for an instance variable (long scope). (Felix Siegrist)
- Page 313, 2nd to last line: "a test that annotated with" should be "a test that is annotated with". (Felix Siegrist)
- Page 318, 2nd code block: "startAllThreadsw" should be "startAllThreads". (Felix Siegrist)
- Page 319, 3rd line: "just change the processMessage" should be "just change the process method". (Felix Siegrist)
- Page 319, 2nd paragraph of section "Server Observations": "so the we could" should be "so we could". (Felix Siegrist)
- Page 323, last paragraph of section "Number of Paths": "N! in the general case" should be ". and T! in the general case". The use of "synchronized" makes the eight lines of byte-code to be executed as if they were only one. So, N = 8 becomes N = 1. Replacing N = 1 in the formula (NT)!/(N!**T), it becomes (1T)!/(1!**T) = T! (Filipe Silva)
- Page 324, code block: "value" should be replaced twice with "lastId". (Felix Siegrist)
- Page 325, 2nd to last row of 2nd table: "PUTFIELD value" should be "PUTFIELD lastId". (Felix Siegrist)
- Page 327, 2nd code block: getValue() should be synchronized as well. (Joe Bowbeer)
- Page 328, 2nd to last text line (before code block): "HashTable" should be "Hashtable". (Felix Siegrist)
- Page 329: "Lock the HashTable" should be "Lock the Hashtable" and "Wrap the HashTable" should be "Wrap the Hashtable". (Felix Siegrist)
- Page 330, first paragraph: "IngeterIterator" should be "IntegerIterator". (Joe Bowbeer)
- Page 334, 2nd paragraph: "An instance of the PageIterator can be shared between many different threads, each one using it's own instance of the PageReader to" should be "An instance of the PageIterator can be shared between many different threads, each one using the same instance of the PageReader to". Seeing as they are all using the same instance of the PageIterator, then they must share the same instance of the PageReader, as it was passed in the construction of the PageIterator. (Adrian Domnul Racu)
- Page 336, 2nd paragraph, bulleted items: The words "create" and "update" should be mutually exchanged. (Felix Siegrist)
- Page 336, 2nd paragraph: "interrupted" should be "blocked". "blocked" as in waiting for a resource. If the threads were actually interrupted, there would be no deadlock. (Joe Bowbeer)
- Page 341, table: "return end" should be "end". (Joe Bowbeer)
- Page 344: Listing A-4 does not show code of ClientTest.java (it repeats Server.java). (Felix Siegrist)
- Page 346, first paragraph: "process message" should be "process method". (Felix Siegrist)
- Appendix C: The chapter/page numbers of the cross references are all wrong. (Felix Siegrist) A replacement Appendix C has been provided by Dan Hepler.
I created this page because there is apparently no official errata page for this book. If you have any errata to share, please e-mail me (Daniel Walls) at clean-code-errata@mh0.org. I will give attribution unless you don't want it.