Untitled.Bat |
Developer, dad & dangerously dopey.....Also awesome at alliteration and assonance . Visit my main website @ http://www.xerxesb.com if you're tech-inclined |
T1: Insufficient Tests - A test suite should test everything that could possibly break. The tests are insufficient so long as there are conditions that have not been explored by the tests.
T2: Use a Coverage Tool - Coverage tools report gaps in your testing strategy
T3: Don’t Skip Trivial Tests - They are easy to write and their documentary value is higher than the cost to produce them
T4: An Ignored Test Is A Question About an Ambiguity - Sometimes we are uncertain about a behavioural detail because the requirements are unclear. We express this questioning as a test which is commented out or marked as [Ignore].
T5: Test Boundary Conditions - Take special care to test boundary conditions. We often get the middle of an algorithm right, but misjudge teh boundaries
T6: Exhaustively Test Near Bugs - Bugs tend to congregate. When a bug is found, it is best to comprehensively write tests to cover the function - bugs don’t hang around alone!
T7: Patterns Of Failure Are Revealing - Sometimes you can diagnose a problem by finding patterns in the way the test cases fail. This is another argument for making the test cases as complete as possible. It will help to lead to an “aha!” moment when analysing test failures.
T8: Test Coverage Patterns Are Revealing - Looking at code that is or is not executed by the passing tests gives clues as to why the failing tests fail.
T9: Tests Should Be Fast - A slow test is a test that won’t get run. When things get tight, the slow tests will be the first to drop. Do everything you can to keep the tests small and fast
Clean Code: Chapter 17
N1: Choose Descriptive Names - Dont be too quick to choose a name. Make sure the name is descriptive. Names are what makes 90% of the code readable.
N2: Choose Names At The Appropriate Level Of Abstraction - Don’t pick names that communicate implementation; choose names that reflect the level of abstraction of the class or function you were working in?
N3: Use Standard Nomenclature When Possible - Follow existing conventions in the code when used. For instance, calling all controllers the {something}Controller keeps names consistent.. Each project has it’s own conventions for naming - these are known as the ubiquitous language of the project.
N4: Unabmiguous Names - Choose names that make the workings of the function or variable unambiguous.
N5: Use Long Names For Long Scopes - The length of the name should be related to the scope. Tiny variables can have short names but wider scoped members should have longer names.
N6: Avoid Encodings - Names should not be encoded with type or scope prefixes. Hungarian Pollution.
N7: Names Should Describe Side Effects - Names should describe everything that a function does, including side-effects. Don’t hide side effects within the name.
Clean Code - Chapter 17
G28: Encapsulate Conditionals - Boolean logic is hard to understand without having to see it in the context of an IF..THEN. Extract the boolean logic into a function which clearly expresses the intention of the conditional from the clause which executes it.
G29: Avoid Negative Conditionals - Negatives are harder to understand than positives. Conditional operations should be expressed as positives where possible.
G30: Functions Should Do One Thing - It is often tempting and easy to create functions that have multiple sections that perform a series of operations. Functions should do one thing and should be split up accordingly.
G31: Hidden Temporal Couplings - Temporal couplings are often necessary, but they should not be hidden. For instance, if one method relies on the execution of another one before it in order to maintain state, there is a temporal coupling between the two (ie: the sequence of calls). The hidden sequence coupling could be broken if a caller doesn’t make the calls in the right order. You can expose temporal couplings like this by making subsequent calls stateless and take in any data they need to work on instead of relying on common state. This makes it clear that there is a coupling between the two methods
G32: Don’t Be Arbitary - If code layout and organisation conventions are followed and maintained throughout the codebase, then there is less chance that people will come along and make arbitary changes against the conventions. If there are arbitary random differences, it empowers people to break conventions.
G33: Encapsulate Boundary Conditions - Boundary conditions are hard to keep track of. Put the processing in them for one place. EG: if the value “Level + 1” is mentioned in a number of places, then encapsulate it in a variable.
G34: Functions Should Desccend Only One Level Of Abstraction - The statements within a finctions should all be written at the same level of abstraction, which should be one level below the operation described by the name of the function. Though it seems simple, humans are just far too good at seamlessly mixing levels of abstraction.
G35: Keep Configurable Data At High Levels - Constants such as defaults or configuration values that are known and expected at a high level of abstraction, should not be buried in a low-level function. They should be exposed to the low-level function by way of a parameter that is passed into it.
G36: Avoid Transitive Navigation - Modules should only know about their immediate collaborators and do not know the navigation map of the whole system. The Law Of Demeter is what prevents getA().getB().getC().Something();. Breaking is is how architectures become rigid - they know too much about other modules and breaking that dependency is difficult.
Clean Code: Chapter 17
Clean Code: Chapter 17
G19: Use Explanatory Variables - Break up calculations into intermediate values that are held in variables with meaningful names, rather than inlining them. More explanatory variables are generally better than fewer
G20: Function Names Should Say What They Do - Function names should clearly express what the operation will do. If you have to look at the documentation to find out what it does then you should work to find a better name. (eg: date.add(5) - does this add 5 days, 5 minutes? Does is it modify the existing date or does it return a new one?)
G21: Understand The Algorithm - Programming is often an exploration. You think you know the right algorithm to solve a problem but you poke and prod until you get it to work. Once it works though, you *must* spend the time to understand exactly *why* it works. Refactor the function so that it becomes obvious why the implementation works.
G22: Make Logical Dependencies Physical - If one module depends on another, then the dependent module should not make assumptions (aka logical dependencies) about the module it depends upon. Rather, it should explicitly ask the module for all information it depends upon.
G23: Prefer Polymorphism to If/Else or Switch/Case - There may be not more than one switch statmeent for a given type of selection. The cases in that switch statement must create a polymorphic object that takes the place of other such switch statements in the rest of the system.
G24: Follow Standard Conventions - Every team should follow a coding standard based on common industry norms. There should not be a need to document this these conventions because the code provides the examples.
G25: Replace Magic Numbers With Named Constants - It is bad to have raw numbers in your code. You should hide them behind well-named constants. THis allows the code to be more expressible and the intent clearer. This also applies to magic strings.
G26: Be Precise - When you make a decision in your code, make sure you make it precisely. Know why you have made it and how you will deal with exceptions. If you decide to call a function that might return a null, make sure you check for null. Ambiguity and imprecision in code is either a result of disagreements or laziness.
G27: Structure Over Convention - Enforce design decision with structure over convention. Naming conventions are good, but they are inferior to structures that force compliance. For example, switch/cases with nicely named enumerations are inferior to base classes with abstract methods. Noone is forced to implement the switch/case statement the same way each time, but the base classes do enforce that concrete classes have all abstract methods implemented.
Clean Code: Chapter 17
G10: Vertical Separation - Variables and private functions should be defined close to where they are used. Local variables should be declared just above where they are used and private functions should be defined just below where they are used. We would like to limit the vertical distance between invocations and definitions.
G11: Inconsistency - If you do something a certain way, do all similar things in the same way. This refers to The Principle Of Least Surprise. Simple consistency, when readily applied, can make code much easier to read and modify
G12: Clutter - Keep source files clear of clutter. This includes empty default constructors, fields and functions that are never used, comments that add no value.
G13: Artificial Coupling - Artificial coupling is coupling between two modules that serves no direct purpose, It is a result of putting a variable, constant or function in a temporarily convenient, though inappropriate location. This is lazy and careless. Take time to define where these members should be defined and find the most appropriate place for them
G14: Feature Envy - When a method uses accessors and mutators of some other object to manipulate data within that object, then it envies the scope of the class of the other object. The methods of a class should only be interested in the variables and functions on that class itself, and not the internals of another. We want to eliminate Feature Envy because it exposes the internals of one class to another.
G15: Selector Arguments - Selector Arguments are just a lazy way of avoiding splitting a function into several smaller ones. A parameter to the function (either bool, or other type) is used to select the behaviour of the method (eg: CalculateWage(bool includeOvertime) ). It is better to have many functions than to pass some code into a function to select some behaviour.
G16: Obscured Intent - Code should be as expressive as possible. Run-on expressions, hungarian notation and magic numbers all obscure the author’s intent.
G17: Misplaced Responsibility - The most important decision a developer can make is where to put code. Based on The Principle Of Least Surprise, the code should be placed where a reader would naturally expect it to be.
G18: Inappropriate Static - In general you should prefer non-static methods to static methods. If in doubt, make it non-static. Only make a method static if you don’t want it to behave polymorphically, it doesn’t rely on any state, and it gets all its information from the arguments passed into the method.
Clean Code: Chapter 17
G1: Multiple Languages In One Source File - Mixing more than one language in a single file is confusing and carelessly sloppy. JSP, HTML, XML, English they all contribute to making things less clear. Ideally, keep each file in one language (at most 2)
G2: Obvious Behaviour Is Unimplemented - Based on The Principle of Least Surprise, any function or class should implement the behaviours that another programmer could reasonably expect. When an obvious behaviour is not implemented, readers and users of the code can no longer depend on their intuition about function names and behaviours.
G3: Incorrect Behaviour At Boundaries - Programmers open write functions that they think will work but often fail at the boundary conditions and corner cases. There is no replacement for due diligence - every corner, boundary condition, quirk and exception should be discovered and test-covered.
G4: Overridden Safeties - It is risky to override safeties like compiler warnings or failing tests. You might get immediate benefit by ignoring the problems in front of you, but will likely create much larger problems down the road.
G5: Duplication - Every time you see a duplication in code, it shows a missed opportunity for abstraction (perhaps a method or a new class). By folding the duplication into an abstraction you increase the vocabulary of the language of your design. Most obvious forms of duplication are copy/pasted code which could be abstracted into simple methods. Next most obvious form is switch/if..else blocks which should be replaced with polymorphism. Less obvious are routines which share a similar algorithm, but have different implementations and could use a Strategy or Template design pattern. Most design patterns are actually just well-known ways of removing duplication in code.
G6: Code At The Wrong Level Of Abstraction - It is important to create abstractions that separate higher level concepts from lower level detailed concepts. We want all low-level (detail) concepts to be in the derivative classes, and all high-level concepts to be in the base classes.
G7: Base Classes Depending on Their Derivatives - The most common reason to partition concepts into base and derivative classes is so that high-level concepts (base concepts) can be independent of the lower level concepts, and the base and derivatives can be deployed independently of each other. Where a derivative concept is found in a base class we generally smell a problem. The exception to this is when there are a finite number of tightly coupled/controlled derivatives for a base implementation, and will always be deployed together, however we want to minimise this as much as possible.
G8: Too Much Information - Help keep coupling low be limiting information. Interfaces should be small, and have few methods, additionally with few arguments. Implementations should have few instance variables. Hide data, constants, temporaries. A well-defined interface doesn’t provide many functions to depend upon.
G9: Dead Code - Dead code is code that isn’t executed. This might be because it is commented out, or because it exists in an IF block which never evaluates to true, or it exists in a catch block of a try..catch which never throws. Dead code starts to smell. The older it is, the more it smells because noone is updating the code with newer conventions and rules. The best thing for dead code is to give it a decent burial (and 21 gun salute)
Clean Code: Chapter 17
F1: Too Many Arguments - Functions should have small number of arguments. No argument is best, followed by one, two and three. More than 3 should be avoided with prejudice and is very questionable
F2: Output Arguments - Output args are counterintuitive. Readers expect arguments to be inputs not outputs.
F3: Flag Arguments - Boolean arguments loudly declare that the function does more than one thing. They are confusing and should be eliminated
F4: Dead Function - Methods that are never called should be discarded. Keeping dead code is wasteful.
Clean Code: Chapter 17
E1: Build Requires More Than One Step - Building a project should be a single, trivial operation. You should be able to check out the system with one simple command and then issue one other simple command to build it.
E2: Tests Require More Than One Step - Being able to run all tests is so fundamental and so important that it should be quick, easy and obvious to do.
Clean Code: Chapter 17
C1: Inappropriate Information - It is inappropriate for a comment to hold information better held in different kinds of systems such as source-control, issue tracking or other forms of record keeping. Comments should be reserved for technical notes about the code design.
C2: Obsolete Comment - Comments get old quickly. It is best not to write a comment which will become obsolete as they float away from the code once described and become islands of irrelevance and misdirection.
C3: Redundant Comment - A comment which describes something that adequately describes itself. eg: i++; // Increment i
C4: Poorly Written Comment - If it’s worth writing, it’s worth writing well. Choose your words carefully and use correct punctuation and grammar. Don’t ramble Be brief
C5: Commented Out Code - This code sits and rots, getting less and less relevant every day. It calls functions that no longer exist, uses variables whose names have changed and follows out-dated conventions. Just delete it! your source-control system will still remember it.
Clean Code: Chapter 17
To write clean code, you must first write dirty code and then clean it. Writing clean compositions is a matter of successive refinement.
When you recognise that code is becoming dirty, and that further changes and extension to the code will make it even dirtier, STOP. It’s time to refactor your implementation.
Start small and use small, successive increments to improve (refactor) the code. Using TDD and a unit-testing framework will give you confidence to make changes to your code. Putting code in so you can later take i our again is pretty common in refactoring. Refactoring is a lot like solving a Rubik’s cube - there are lots of little steps to achieve a large goal.
“It is not enough to 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 enough 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 ferments, becoming an inexorable weight that drags the team down. Time and time again I have seen teams grind to a crawl because, in their haste, they created a malignant morass of code that forever thereafter dominated their destiny.” - Bob Martin
Clean Code: Chapter 14