1. Every line of code is a user interface
If this wasn’t true, then there wouldn’t be a point to high-level languages. Imagine a stranger is reading your code and write like you were trying to explain the program to him.
2. Choosing the right name is everything
Having an intuitive, descriptive, accurate name for classes, methods and properties is just as important as what they do. Avoid “ProcessData()” disease.
3. Minimize state
Variables and fields are begrudging necessities. When you’re forced to declare a variable, sneer at it. Consider inlining a function call instead of stashing its output in a temporary variable if it’ll only be called once. But don’t go silly and start recycling variables for different purposes.
4. If it doesn’t return or modify the state of an object, then it should be static
Pass a functions’s parameters to it, don’t pull them out of your class.
5. DestroyBaghdad() is immoral. Use DestroyCity(Baghdad) instead
Avoid tying details to implementations. Check your language to see if it supports first-class functions (functions that can be passed as arguments to other functions) and design a program to push any unavoidably specific logic to the edges.
6. One side effect at a time
A side effect is anything that happens in addition to returning a value from a function. For example, bool MsgBox(string message)
is a classic case: it returns a boolean, but it has the side-effect of creating a UI with an “OK” button. Side effects are not necessarily bad unless you do more than one per function. Don’t manifest UI, change global variables, update a database and then return a boolean.
7. Don’t ENUMerate what hasn’t been implemented
Laundry lists of promised modes and half-empty SWITCHboards shouldn’t be thought of as a skeleton waiting to be fleshed out, because they will force maintenance programmers to keep re-discovering what hasn’t been done yet. They also set a name in stone before you know if it’s right. Leave speculative modes in comments–or better yet, the case tracking system.
8. Don’t use strings or integers if it can be Enumerated
If you’re keeping track of definite states (“Open”, “Closed”, “Initializing”, “Faulted”, etc.) don’t store that state as a string or integer if your language supports ENUMs. If you have to accept a string to set an enumerable state, use the Enum.Parse()
or TryParse()
method to get input validation for free.
9. Keep business objects pure
When you create a new class to represent a business-related concept or chunk of information, such as a SKU or customer, don’t contaminate it with logic relevant only to the program you’re writing at the time. Keep it purely about the thing it models and nothing else. For example, if you have a class that models a customer and you happen to be using it in a program that fetches PayPal transactions, do not put a GetPayPalTransactions
method in the customer class. Put the GetPayPalTransactions method elsewhere and have it accept a customer object as its parameter.
Side tip: consider using Extension methods in C# or Categories in Objective-C to gain some Intellisense/Tab-Completion advantages in your IDE.
10. Shun concatenation
Favor String.Format()
over string concatenation, especially when preparing SQL. It makes it easier to see what the template is, avoids accidentally invoking arithmetic addition, and can provide better formatting options in most languages.
11. Use list comprehensions instead of for-loops
A list comprehension (“foreach”, “map”, LINQ extensions, etc.) is better than a for-loop unless you really need to know the index position.
12. Make your variables final
In Java, declare your variables with the final
keyword so their values can’t be set more than once at runtime. In C#, use readonly
on as many fields as you can. This will force you to change how you write code and think of variables as labels for the values themselves, rather than for the boxes that contain them.
13. Put constants in configuration files
Do not hard-code directory paths, server names or database names. Most popular development environments now have built-in mechanisms for configuration files, saved in XML or some other format. In Microsoft’s Visual Studio, for example, it’s begins with the “app.config” file and the System.Preferences
namespace.
14. Only UI-behavior belongs in code-behind files
Microsoft’s development tools are heavy on the “code-behind” idea, where you paint a form and then put logic in a class that was automatically generated to go with it. Don’t put business logic in these code-behind classes. Only use them to put the reflexive logic of your user-interface, like basic input validation or enabling/disabling controls based on other inputs and settings.
15. Some cut-n-paste is okay
Use your judgement to cut-n-paste if it makes the action of the program easier to read. The “DRY” principle wasn’t meant to make you obsessively hide every line of code behind abstractions (soon you’ll be cut-n-pasting the abstraction’s invocation syntax). If it makes you feel better you can call them something disarming, like “snippets”.
16. Reuse != borrowing
Code reuse can become code abuse if you borrow classes, enums or interfaces for incidental parts of their function or to reinterpret their meaning. Don’t instantiate a database connection class because you want to use its string quoting/escaping functions to write text files, don’t borrow the System.IO.FileAccess
enum becuase you’re too lazy to make your own definition of Read
and Write
, etc.
17. Don’t be in a hurry to make abstractions
If you make an abstraction the first time you meet a new concept it will be too limited, and attempts to extend it to cover the rest of its natural domain will be hacky and buggy.
18. Exceptions are exceptional
Don’t shun the idea of throwing exceptions, but don’t design a program that throws them ten times a second, either. Nor should you worry about handling every exception at the point where they occur: sometimes the only logical place to recover from them is further up the call-stack than where you are now. Throwing an exception can incur a performance penalty because:
1) it’s like a “GOTO” that forces the computer to load code that isn’t in the cache, and
2) it may involve a ring transition. Both of these penalties are trivial if you throw exceptions rarely
Observations on Code
Functional style in an imperative language only works if the language designer wanted it to
Lambdas, list comprehensions, closures and other functional techniques are the new silver bullets, but not when you try to fake them in a language that wasn’t designed to encourage it (“When in Rome, don’t speak latinized Japanese“).
It doesn’t matter where you put the curly braces
Readability is paramount, but there is no hard-and-fast rule that always applies to every situation. Sometimes it’s easier to read code when you put a whole IF-THEN on one line, and sometimes it’s not. Because “readability” itself is a subjective quality, so is every choice you make to format the code.