Coding Style & Design Guide

This page is a collection of guidelines, advices, notes, tips and recommendations that I found over the years, in books and on the internet; they are not hard and fast rules or laws set in stone!
Some items are (re)written by be, some are paraphrased and many are just copy-&-pasted (forgive me if the attribution is missing; this was originally just a text file where text snippets got dumped into).
The entries are primarily meant to be a reference for me, by me – but maybe someone else may also find a few interesting or thought-provoking items here…
Note on applicability: Most of the items here are platform and language agnostic, but some are more specific or useful for certain ecosystems/paradigms/languages than others; if it makes sense (and I’ll remember it), I’ll mention such cases. Otherwise assume that it mainly stems from a Windows/C++/OOP/Powershell/Python background.
Note on the revision history: The Posted date at the top isn’t fully correct – but I do know that I started the original version of this text document (which is lost now)
around 2006/2007. Around 2010 I reworked it. And another big overhaul happened in 2021.
Since then, it’s under regular inspection and in a Git repository.
Update: Well, it was until 2022-02-25, when The Big Gap™ occured and I continued again on 2025-06-22.
General Remarks
-
First learn to stick by the rules. Then learn when, why, and how not to follow them.
-
The source code of a program is a sort of publication. It is meant to be read and understood by the author or other programmer(s) – which may very well be yourself, again, some time later… –, and only at the end by the compiler and the machine (they will mostly complain loudly anyway if they don’t understand the code).
-
Tabs for indentation, spaces for alignment. (Reference)
UPDATE 2021: Although I was for a long time a proponent of that philosophy, I actually changed my mind on this topic in some time ago; more about it in a blog post of mine. Now it is: Use always spaces. -
Efficiency matters only when it matters. The rest of the time, simplicity, correctness, and maintainability dominate. (Joseph M. Newcomer)
-
Don’t forget to initialize a variable! (once bitten, twice shy; if in doubt: Give it a value.)
-
Use string resources for all user messages, this makes translation to other languages much easier.
-
Before you develop a solution, make sure you really understand the problem. (Raymond Chen)
-
Automate as much as possible (without sacrificing the ability to customize or create better individual solution, if applicable, of course).
Makes life easier (“just remember to push this button” vs. “when deploying, check all the items on this list manually”) and is also helpful with scaling, when the task grows.This is meant for the macro level (automate building/testing/installation steps), as well as the micro level:
Don’t copy and paste a line of code ten times, with slight modifications (error prone!), if a loop is possible, that generates (or looks up) the values on the fly.
On Design
Follow the principle of KISS: Keep it simple & stupid/short/sufficient!
Other mnemonics on design and simplicity:
- When in doubt, leave it out.
- Simple ain’t easy.
Good source code should aim to be (perfection being the ultimate goal):
- pure and simple (just algorithms and data structures)
- correct (… performance of the basic required function)
- robust (handling errors or invalid input, etc.)
- maintainable (including a clear and consistent style; easy to read)
- Reliable (… handling and recovering from internal errors)
- reusable (concept of modularity; flexible)
- portable
- Secure (… in the face of a malicious adversary)
- efficient (fast/small)
Encapsulation – A component should know nothing about its environment that it cannot deduce from
formal interfaces which are independent of any one particular incarnation of that environment.
An object should only know about itself (other information could be accuired by a query).
An object should only know how to fullfill/solve its own task/problem.
When you’re writing code that’s going into a system library, make darned sure that it’s written to be as performant as humanly possible, because you never know if someone’s going to find your one-off piece of code and put it in their innermost loop. (Larry Osterman, 2005)
The open closed principle of object oriented design states: Software entities like classes, modules and functions should be open for extension but closed for modifications.
The Liskov Substitution Principle of object oriented design states: In class hierarchies, it should be possible to treat a specialized object as if it were a base class object.
Seperate the logic from presentation, manipulation. Follow the paradigm of MVC (split between model, view, and controller).
On Comments and Documentation
- Adhere to the six W: What, When, Who, Where, Why, How.
- Code says what to do, comments say why.
- Write in full sentences, follow the rules of grammer and take care of spelling errors.
- Use comments to summarize; don’t just repeat the code.
- Comments aren’t checked by the compiler, so there is no guarantee they’re right, especially after the code is modified.
- Try to communicate intent: Comment at the level of the problem, rather than the level of the solution (code = solution).
- Write for a world-wide audience of programmers. Omit slang or regional expressions.
- The most reliable documentation of software is the code itself.
- The code is often the only documentation. Therefore, make your code self-documenting, and add comments where neccessary.
- If the code is clear, and uses good type names and variable names, it should explain itself.
- Comments that contradict the code are worse than no comments at all; keep them up to date/in sync.
Changelog entries and commit messages
-
Commit messages and comments aren’t substitute for each other; they are complementary.
Source code represents the current state of development; log messages represent a summary/history of the development process. -
The six Ws (see above) should also be satisfied in a log entry:\
- check-in meta-information = when, who
- code/comments (diff) = where, how, why
- log message = what
-
One should never need to read a changelog to understand the current version of the code.
-
Do not abbreviate or truncate names of files, functions, variables, etc. in a log entry – that makes it only harder to find the information again later on.
On Naming and Formatting
Inspired amongst others by e.g. 1.
Misc. notes
- Interestingt talk by Kate Gregory: Naming is Hard: Let’s Do Better (ACCU 2021)
- Length is not a virtue in a name; clarity of expression is. A global variable rarely used may deserve a long name; a loop counter may be very short.
- Consistency is important in naming.
General naming conventions
Type | Description |
---|---|
Classes | Start with a capital letter, divide into parts starting with a capital letter again, e.g. VideoDataBuffer |
Member variables and parameters | Lower case letters, parts may be divided by underscore, e.g. next_edge , colorspace . |
Member functions | First part being a verb, starting with a lower case letter, then divided into parts starting with capital, e.g. getVideoDataBuffer() |
Event Handling | Examples: onMouseButtonL , atMaxValue() , onMaxValue() |
Macros and Constants | All capital, parts divided by underscore, e.g. CLAMP , DEFAULT_SIZE . |
Enums | Like classes, e.g. QueueMode ; no hard naming rules for the entries |
Functions, methods, procedures
- The name of a procedure should reflect what it does; the name of a function should reflect what it will return.
- A method is a fancy name for a function (or procedure) that is a member of a class (as I learned, the C++ community uses and prefers the term member function).
- All those define actions to be performed (upon the object), so the names should have the meaning of a predicate,
and should usually start with an imperative verb; e.g.
getX
,setY
,doThis
,calculateThat
, …
- Interlude: Approved Verbs for PowerShell Commands
- During the last couple of years, I have written more and more Powershell code;
and although this wasn’t a love at first sight, the more I used and learned about it, the more I liked certain aspects
of the language and ecosystem.
For example, I initially found the idea of Approved Verbs for PowerShell Commands terrible: I didn’t want to be patronized or restricted/limited on which names I could/should use. And one isn’t: It’s not enforced, and even the warnings (when importing a module) can be disabled. But by now, I’ve been accustomed to it and even like it (a bit): It offers some common ground and a good starting point when I need to use (or create) a new function. It’s not a perfect solution, but OK.
Here’s a very brief list of a few common verbs:
Verb | Remark |
---|---|
get | Accesses data immediately. For internal data/members, see the note on accessors below. |
set | Declaratively sets a variable or a member to a value. For internal data/members, see the note on mutators below. |
reset | Sets a variable or a member back to its initial value or state. |
fetch | Requests data, which takes time; e.g. establishing a connection, contacting a server, waiting for a reply, downloading data… |
remove | Removes something from somewhere, for example an object from a collection. The objects thembselves may continue to live, but outside of the collection. |
delete | Completely erases something from the realm of existence. |
compose | Creates new data from existing data. |
handle | Handles an action. Often used when naming a callback method. |
on | Not really a verb, but I also often use (and see elsewhere) the on* prefix for signal handlers, e.g. onClicked(…), and think it fits. |
Getter & Setter vs. Accessor & Mutator
Some data members should be private and not reachable from the outside, so external/direct access is disbled or at least discouraged.
Or because the only correct way to get a valid result is to combine from/calculate with multiple elements,
so simply reading or writing the value of an int
straight to the member may not be OK.
The proper way then is usually encapsulate the access by public getter and setter functions, like int getData(void) and void setData(int), where the noun is the name of the property that they will effect.
An alternative, which I’ve seen more often over the years, is to save the verb completely: Without parameters, it’s the accessor, and with (at least) one parameter, it’s the mutator:
int getStatus() // Variant A: Getter
int Status() // Variant B: Accessor
void setStatus(int value) // Variant A: Setter
void Status(int value) // Variant B: Mutator
I haven’t actually made my mind up yet on which I prefer; both have their pros and cons:
-
The verbs for Getter/Setter are pretty much just filler material.
On the other hand: That way, they do fit into the rest of the usual style guides: a function should start with an imperative verb. -
The Accessor/Mutator variants are easier to type, but I might have to spend a milisecond on each encounter on interpreting what they actual do:
“No argument? Oh, right, it’s an assignment… => the Accessor is being used”
But that may very well be just be a matter of time and acclimatization.
On other side, like mentioned before, a function without an imperative verb breaks the convention
Functions returning a boolean value
A function that returns a boolean value (i.e. true or false) should be named with a modal/auxiliary verb, which brings it into an interrogative form; i.e. it should read like a short polar question (yes-or-no-question); just imagine a question mark at the end.
A few examples:
AuxVerb | Remark |
---|---|
is/was/will | Describes the existence of a characteristic or state. Depending on the context, past or future tense versions (was, will) may be more appropriate.isAvailable(), isValid(), wasInterrupted(), willBlock() |
has/can | Describes whether the current context possesses a certain attribute (value, state):hasEnoughEnergy(), hasMembers(), canFly() |
does | Describes whether the current context is capable of a certain [positive] action:doesReturn() |
should | Reflects a [positive] conditional statement (usually a boolean), coupled with a certain action:shouldUpdate(), shouldContinue() |
Generally avoid inserting not between the words to define negative logic; instead express that in code: use the positive logic names prefixed with the language ! operator. |
But don’t go overboard with the aspect of “phrase it like a question”:
bool has_entries(); // OK
bool are_entries_present(); // Why so verbose?
As for any function name: Save redundant components (like repeating the same stuff in a class method):\
List foo;
bool x = foo.isListEmpty(); // Hmm...
bool y = foo.isEmpty(); // OK
We could shorten that one even more so – but then we’re getting on thin ice with some words:
bool z = foo.empty()
- Breaks the convention (like accessor/mutator, see above) that method names should begin with an verb.
- Leads to ambiguity and misunderstanding: Does it answer the question Is the list empty? or does it perform the action Do empty the list! (and if it also returns a bool for ‘success’ or ‘failure’, not even the prototype will help us understand).
The terms Property and Attribute
I’ve noted in different languages and systems the varying use of the two terms property and attribute:
In common usage/speak/talks, they are mostly used synonymously, but since programming is all about details,
I spent some time reading up on those words; my understanding is currently this:
First of all, this is what was on Wikipedia on 2021-05-01:
In computing, an attribute is a specification that defines a property of an object, element, or file. It may also refer to or set the specific value for a given instance of such. For clarity, attributes should more correctly be considered metadata. An attribute is frequently and generally a property of a property. However, in actual usage, the term attribute can and is often treated as equivalent to a property depending on the technology being discussed. An attribute of an object usually consists of a name and a value; of an element, a type or class name; of a file, a name and extension.
That, plus some more random samples from the internet, lead me to this table, which will hopefully be a good reminder for me…
Property | Attribute |
---|---|
“Color” | “Black” |
Inherit characteristic of an entity | Additional information about an entity |
Belonging to a class (of an entity) | Belonging to an instance of such a class |
Comparision: Like a parameter | Comparision: Like an argument value |
The terms Resource and Asset
Another pair of terms that gets often lumped together is resource and asset; often in terms of a project directory structure, but also elswhere in the code (are we fetching and loading a resource or an asset?).
In some project layouts, each one gets its own distinct folder (that’s my current preference); but in other versions, “assets” is a subdirectory of “resources”, or vice versa.
As before, the following table contains no hard-and-fast rules, but only my currently applied points of reference on when to use which term for what – which already shows a leak: Icons. It fits in the “asset” category (binary, static), but on the other hand, in almost all codebases that I’ve seen, they are treated and named as “resources”…
Resource | Asset |
---|---|
Often a text file | Often a binary file |
Content that can change or is used in/as code, e.g. a configuration file, a template file, etc. |
Content that is static/invariable/constant. |
File types: * icon(?) * text file |
File types: * icon(?) * binary blob * image/graphic * video * audio/sound |
The terms Find and Search
The terms Silent, Quiet and Verbose
These terms are often used as parameters of programs (i.g. installation setups), but I have also it as names for parameters of Powershell functions (in my day job):
- If used interactively, by a user, the function should display status or progress messages.
- But when called from within another function or script, I don’t want or need that noise on the console; then a final boolean return value is often enough.
Although silent and quiet are normally more used in relation to audio/sound, it’s not uncommon (in “computing”) to extend the meaning also to other [visual] feedback, e.g. text messages on a console.
Output | Term | Description |
---|---|---|
0% | Silent | Free from sound or noise; absolutely still; perfectly quiet. |
50% | Quiet | With little (or no) sound; free of disturbing noise. |
75% | Normal | Random value and term, just to compare and clarify the other terms… |
100% | Verbose | using more words than necessary; long-winded, wordy (computing) Producing detailed output for diagnostic purposes. |
Prefer the east const style
The rules for using the keywords const
and volatile
can sometimes a bit tricky, so for simplicity’s sake,
just use the “east const” style.
As a mnemonic, remember this:
The const qualifier is applied to what’s on its left; if there is nothing of its left, then it is applied to what it is on its right. 2
That ‘if…’ clause alone is reason for me to keep it even simpler ;-)
Therefore, it’s recommended to put const
always on the right side (in the “east”) of the item that should become constant.
The east const style is also consistent with the way how constant member functions are declared:
with the const
qualifier on the right:
int get() const;
Here are a few pro-west-const/anti-east-const arguments and why I don’t think they are valid:
- “West const is more familiar”: Well, then no change would ever gain traction (or win) against the inertia of the world.
- “East const code doesn’t read like english”: Well, C++ code has some similarities with english (keywords), but it’s not english prose anyway.
Translate the following code lines as being read from right to left (confirm with the comments below):
int const n; // n is a constant integer
int* p1; // p1 is a (mutable) pointer to a (mutable) integer
int* const p2; // p2 is a constant pointer to a (mutable) integer
int const* p3; // p3 is a (mutable) pointer to a constant integer
int const* const p4; // p4 is a constant pointer to a constant integer
On Building and Testing
Use at least two different compilers! Some problems are just a quirk of the tool, not a flaw in your logic.
Use the maximum warning level your compiler has.
If possible set compiler warnings as errors, this ensures accidental warnings need to be fixed.
Have a daily build process that builds the entire system and if possible have some automated regression tests.
Frequently perform project builds on a clean machine with limited network access by following development environment configuration instructions, and then getting and building from source control. During this process many teams discover hard coded paths, missing dependencies, and solution defects. It’s better to detect it early than for development to grind to a halt the day before a big release when the corporate IT department decommissions that old server that supposedly wasn’t in use for the past year.
Don’t guess where your code is slow, use a profiler. Either use the one that comes with your compiler or try Numega’s TrueTime.
Appendix
From What Makes Good Code Good?
By Paul DiLascia, taken from MSDN Magazine, July 2004 issue
When MSDN Magazine asked me to write a page on something I care about, I said, “You mean, like abolishing taxes, car phones, and SUVs?” Alas, they meant something to do with programming. Well! After pondering, I realized that something I care about is writing good code. Books and magazines spare no space explaining how to program the latest API or marshal objects from Redmond to Zwaziland, but how to write good code? What is good code, anyway?
A good program works flawlessly and has no bugs. But what internal qualities produce such perfection? It’s no mystery, we just need some occasional reminding. Whether you code in C/C++, C#, Java, Basic, Perl, COBOL, or ASM, all good programming exhibits the same time-honored qualities: simplicity, readability, modularity, layering, design, efficiency, elegance, and clarity.
Simplicity means you don’t do in ten lines what you can do in five. It means you make extra effort to be concise, but not to the point of obfuscation. It means you abhor open coding and functions that span pages. Simplicity – of organization, implementation, design – makes your code more reliable and bug free. There’s less to go wrong.
Readability means what it says: that others can read your code. Readability means you bother to write comments, to follow conventions, and pause to name your variables wisely. Like choosing “taxrate” instead of “tr”.
Modularity means your program is built like the universe. The world is made of molecules, which are made of atoms, electrons, nucleons, quarks, and (if you believe in them) strings. Likewise, good programs erect large systems from smaller ones, which are built from even smaller building blocks. You can write a text editor with three primitives: move, insert, and delete. And just as atoms combine in novel ways, software components should be reusable.
Layering means that internally, your program resembles a layer cake. The app sits on the framework sits on the OS sits on the hardware. Even within your app, you need layers, like file-document-view-frame. Higher layers call ones below, which raise events back up. (Calls go down; events go up.) Lower layers should never know what higher ones are up to. The essence of an event/callback is to provide blind upward notification. If your doc calls the frame directly, something stinks. Modules and layers are defined by APIs, which delineate their boundaries. Thus, design is critical.
Design means you take time to plan your program before you build it. Thoughts are cheaper than debugging. A good rule of thumb is to spend half your time on design. You need a functional spec (what the programs does) and an internal blueprint. APIs should be codified in writing.
Efficiency means your program is fast and economical. It doesn’t hog files, data connections, or anything else. It does what it should, but no more. It loads and departs without fuss. At the function level, you can always optimize later, during testing. But at high levels, you must plan for performance. If the design requires a million trips to the server, expect a dog.
Elegance is like beauty: hard to describe but easy to recognize. Elegance combines simplicity, efficiency, and brilliance, and produces a feeling of pride. Elegance is when you replace a procedure with a table, or realize that you can use recursion – which is almost always elegant:
int factorial(int n)
{
return n==0 ? 1 : n * factorial(n-1);
}
Clarity is the granddaddy of good programming, the platinum quality all the others serve. Computers make it possible to create systems that are vastly more complex than physical machines. The fundamental challenge of programming is managing complexity. Simplicity, readability, modularity, layering, design, efficiency, and elegance are all time-honored ways to achieve clarity, which is the antidote to complexity.
Clarity of code. Clarity of design. Clarity of purpose. You must understand – really understand – what you’re doing at every level. Otherwise you’re lost. Bad programs are less often a failure of coding skill than of having a clear goal. That’s why design is key. It keeps you honest. If you can’t write it down, if you can’t explain it to others, you don’t really know what you’re doing.
There’s so much I’ve left out, but there’s one more thing I hesitate to add. Use it sparingly and only in desperation: the clever hack. The clever hack is when you sacrifice your principles to expedience. When you hardcode some condition or make a call up the layer cake–or commit some other embarrassment–because it works and there’s no time to do it right. But remember: it must be clever! It’s the cleverness that redeems the hack and gives it a kind of perverse elegance. And if the hack doesn’t work, don’t blame me! Happy programming!
From Basics of the Unix Philosophy
Taken from “The Art of Unix Programming”, Chapter 1, by Eric Steven Raymond.
Doug McIlroy: “This is the Unix philosophy: Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.”
- Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new features.
- Expect the output of every program to become the input to another, as yet unknown, program. Don’t clutter output with extraneous information. Avoid stringently columnar or binary input formats. Don’t insist on interactive input.
- Design and build software, even operating systems, to be tried early, ideally within weeks. Don’t hesitate to throw away the clumsy parts and rebuild them.
- Use tools in preference to unskilled help to lighten a programming task, even if you have to detour to build the tools and expect to throw some of them out after you’ve finished using them.
Rob Pike (in “Notes on C Programming”):
- You can’t tell where a program is going to spend its time. Bottlenecks occur in surprising places, so don’t try to second guess and put in a speed hack until you’ve proven that’s where the bottleneck is.
- Measure. Don’t tune for speed until you’ve measured, and even then don’t unless one part of the code overwhelms the rest.
- Fancy algorithms are slow when n is small, and n is usually small. Fancy algorithms have big constants. Until you know that n is frequently going to be big, don’t get fancy. (Even if n does get big, use Rule 2 first.)
- Fancy algorithms are buggier than simple ones, and they’re much harder to implement. Use simple algorithms as well as simple data structures.
- Data dominates. If you’ve chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.[9]
Rule of…
- Rule of Modularity: Write simple parts connected by clean interfaces.
- Rule of Clarity: Clarity is better than cleverness.
- Rule of Composition: Design programs to be connected to other programs.
- Rule of Separation: Separate policy from mechanism; separate interfaces from engines.
- Rule of Simplicity: Design for simplicity; add complexity only where you must.
- Rule of Parsimony: Write a big program only when it is clear by demonstration that nothing else will do.
- Rule of Transparency: Design for visibility to make inspection and debugging easier.
- Rule of Robustness: Robustness is the child of transparency and simplicity.
- Rule of Representation: Fold knowledge into data so program logic can be stupid and robust.
- Rule of Least Surprise: In interface design, always do the least surprising thing.
- Rule of Silence: When a program has nothing surprising to say, it should say nothing.
- Rule of Repair: When you must fail, fail noisily and as soon as possible.
- Rule of Economy: Programmer time is expensive; conserve it in preference to machine time.
- Rule of Generation: Avoid hand-hacking; write programs to write programs when you can.
- Rule of Optimization: Prototype before polishing. Get it working before you optimize it.
- Rule of Diversity: Distrust all claims for “one true way”.
- Rule of Extensibility: Design for the future, because it will be here sooner than you think.
From Practice of Programming (Collected Rules)
Note: I haven’t read the book. The following summary was compiled by someone else and is floating through the net.
Several chapters of “The Practice of Programming by B.W. Kernighan and R. Pike” contain rules or guidelines that summarize a discussion and are listetd in the Appendix of that book. Kernighan and Pike warn that the points were collected for easy reference and that each was presented in a context that explains its purpose and applicability.
“Simplicity, Clarity, Generality.”
– Kernighan and Pike
“Each truth that I discovered became a rule that served me afterwards in the discovery of others.”
– Rene Descartes, Le Discours de la Methode
Style
- Use descriptive names for globals, short names for locals.
- Be consistent.
- Use active names for functions.
- Be accurate.
- Indent to show structure.
- Use the natural form for expressions.
- Parenthesize to resolve ambiguity.
- Break up complex expressions.
- Be clear.
- Be careful with side effects.
- Use a consistent indentation and brace style.
- Use idioms for consistency.
- Use else-ifs for multi-way decisions.
- Avoid function macros.
- Parenthesize the macro body and arguments.
- Give names to magic numbers.
- Define numbers as constants, not macros.
- Use character constants, not integers.
- Use the language to calculate the size of an object.
- Don’t belabor the obvious.
- Comment functions and global data.
- Don’t comment bad code, rewrite it.
- Don’t contradict the code. Clarify, don’t confuse.
Interfaces
- Hide implementation details.
- Choose a small orthogonal set of primitives.
- Don’t reach behind the user’s back.
- Do the same thing the same way everywhere.
- Free a resource in the same layer that allocated it.
- Detect errors at a low level, handle them at a high level.
- Use exceptions only for exceptional situations.
Debugging
- Look for familiar patterns.
- Examine the most recent change.
- Don’t make the same mistake twice.
- Debug it now, not later.
- Get a stack trace.
- Read before typing.
- Explain your code to someone else.
- Make the bug reproducible.
- Divide and conquer.
- Study the numerology of failures.
- Display output to localize your search.
- Write self-checking code.
- Write a log file.
- Draw a picture.
- Use tools.
- Keep records.
Testing
- Test code at its boundaries.
- Test pre- and post-conditions.
- Use assertions.
- Program defensively.
- Check error returns.
- Test incrementally.
- Test simple parts first.
- Know what output to expect.
- Verify conservation properties.
- Compare independent implementations.
- Measure test coverage.
- Automate regression testing.
- Create self-contained tests.
Performance
- Automate timing measurements.
- Use a profiler.
- Concentrate on the hot spots.
- Draw a picture.
- Use a better algorithm or data structure.
- Enable compiler optimizations.
- Tune the code.
- Don’t optimize what doesn’t matter.
- Collect common subexpressions.
- Replace expensive operations by cheap ones.
- Unroll or eliminate loops.
- Cache frequently-used values.
- Write a special-purpose allocator.
- Buffer input and output.
- Handle special cases separately.
- Precompute results.
- Use approximate values.
- Rewrite in a lower-level language.
- Save space by using the smallest possible data type.
- Don’t store what you can easily recompute.
Portabillty
- Stick to the standard.
- Program in the mainstream.
- Beware of language trouble spots.
- Try several compilers.
- Use standard libraries.
- Use only features available everywhere.
- Avoid conditional compilation.
- Localize system dependencies in separate files.
- Hide system dependencies behind interfaces.
- Use text for data exchange.
- Use a fixed byte order for data exchange.
- Change the name if you change the specification.
- Maintain compatibility with existing programs and data.
- Don’t assume ASCII.
- Don’t assume English.
From The Pragmatic Programmer Quick Reference Guide (with additions)
This page summarizes the tips and checklists found in the book “The Pragmatic Programmer” by Andrew Hunt and David Thomas.
Attention: With Additions by me, in the form of {Text – Sascha}
-
Care about your craft
Why spend your life developing software unless you care about doing it well? -
Think! (About your work)
Turn off the autopilot and take control. Constantly critique and appraise your work. -
Provide options, don’t make lame excuses
Instead of excuses, provide options. Don’t say it can’t be done; explain what can be done. -
Don’t live with broken windows
Fix bad designs, wrong decisions, and poor code when you see them. -
Be a catalyst for change
You can’t force change on people. Instead, show them how the future might be and help them participate in creating it. -
Remember the big picture
Don’t get so engrossed in the details that you forget to check what’s happening around you. -
Make quality a requirements issue
Involve your users in determining the project’s real quality requirements. -
Invest regularly in your knowledge portfolio
Make learning a habit. -
Critically analyze what you read and hear
Don’t be swayed by vendors, media hype, or dogma. Analyze information in terms of you and your project. -
It’s both what you say and the way you say it
There’s no point in having great ideas if you don’t communicate them effectively. -
DRY: Don’t Repeat Yourself
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. -
Make it easy to reuse
If it’s easy to reuse, people will. Create an environment that supports reuse. -
Eliminate effects between unrelated things
Design components that are self-contained. independent, and have a single, well-defined purpose. -
There are no final decisions
No decision is cast in stone. Instead, consider each as being written in the sand at the beach, and plan for change. -
Use tracer bullets to find the target
Tracer bullets let you home in on your target by trying things and seeing how close they land. -
Prototype to learn
Prototyping is a learning experience. Its value lies not in the code you produce, but in the lessons you learn. -
Program close to the problem domain
Design and code in your user’s language.
{Or that of the domain in which it will be used; try to avoid terms that are too technical or implementation-specific (that will also help in case you need to modify/refactor it later). – Sascha} -
Estimate to avoid surprises
Estimate before you start. You’ll spot potential problems up front. -
Iterate the schedule with the code
Use experience you gain as you implement to refine the project time scales. -
Keep knowledge in plain text
Plain text won’t become obsolete. It helps leverage your work and simplifies debugging and testing. -
Use the power of command shells
Use the shell when graphical user interfaces don’t cut it. -
Use a single editor well
The editor should be an extension of your hand; make sure your editor is configurable, extensible, and programmable. -
Always use source code control
Source code control is a time machine for your work–you can go back. -
Fix the problem, not the blame
It doesn’t really matter whether the bug is your fault or someone else’s–it is still your problem, and it still needs to be fixed. -
Don’t panic when debugging
Take a deep breath and THINK! about what could be causing the bug. -
“select” isn’t broken.
It is rare to find a bug in the OS or the compiler, or even a third-party product or library.
The bug is most likely in the application. -
Don’t assume it–prove it
Prove your assumptions in the actual environment– with real data and boundary conditions. -
Learn a text manipulation language.
You spend a large part of each day working with text. Why not have the computer do some of it for you? -
Write code that writes code
Code generators increase your productivity and help avoid duplication. -
You can’t write perfect software
Software can’t be perfect. Protect your code and users from the inevitable errors. -
Design with contracts
Use contracts to document and verify that code does no more and no less than it claims to do. -
Crash early
A dead program normally does a lot less damage than a crippled one. -
Use assertions to prevent the impossible
Assertions validate your assumptions. Use them to protect your code from an uncertain world. -
Use exceptions for exceptional problems
Exceptions can suffer from all the readability and maintainability problems of classic spaghetti code. Reserve exceptions for exceptional things. -
Finish what you start
Where possible, the routine or object that allocates a resource should be responsible for deallocating it. -
Minimize coupling between modules
Avoid coupling by writing “shy” code and applying the Law of Demeter. -
Configure, don’t integrate
Implement technology choices for an application as configuration options, not through integration or engineering. -
Put abstractions in code, details in metadata
Program for the general case, and put the specifics outside the compiled code base. -
Analyze workflow to improve concurrency
Exploit concurrency in your user’s workflow. -
Design using services
Design in terms of services–independent, concurrent objects behind well-defined, consistent interfaces. -
Always design for concurrency
Allow for concurrency, and you’ll design cleaner interfaces with fewer assumptions. -
Separate views from models
Gain flexibility at low cost by designing your application in terms of models and views. -
Use blackboards to coordinate workflow
Use blackboards to coordinate disparate facts and agents, while maintaining independence and isolation among participants. -
Don’t program by coincidence
Rely only on reliable things. Beware of accidental complexity, and don’t confuse a happy coincidence with a purposeful plan. -
Estimate the order of your algorithms
Get a feel for how long things are likely to take before you write code. -
Test your estimates
Mathematical analysis of algorithms doesn’t tell you everything. Try timing your code in its target environment. -
Refactor early, refactor often
Just as you might weed and rearrange a garden, rewrite, rework, and re-architect code when it needs it. Fix the root of the problem. -
Design to test
Start thinking about testing before you write a line of code. -
Test your software, or your users will
Test ruthlessly. Don’t make your users find bugs for you. -
Don’t use wizard code you don’t understand
Wizards can generate reams of code. Make sure you understand all of it before you incorporate it into your project. -
Don’t gather requirements–dig for them
Requirements rarely lie on the surface. They’re buried deep beneath layers of assumptions, misconceptions, and politics. -
Workwith a user to think like a user
It’s the best way to gain insight into how the system will really be used. -
Abstractions live longer than details
Invest in the abstraction, not the implementation. Abstractions can survive the barrage of changes from different implementations and new technologies. -
Use a project glossary
Create and maintain a single source of all the specific terms and vocabulary for a project. -
Don’t think outside the box–find the box
When faced with an impossible problem, identify the real constraints. Ask yourself: “Does it have to be done this way? Does it have to be done at all?” -
Start when you’re ready.
You’ve been building experience all your life. Don’t ignore niggling doubts. -
Some things are better done than described
Don’t fall into the specification spiral–at some point you need to start coding. -
Don’t be a slave to formal methods.
Don’t blindly adopt any technique without putting it into the context of your development practices and capabilities. -
Costly tools don’t produce better designs
Beware of vendor hype, industry dogma, and the aura of the price tag. Judge tools on their merits. -
Organize teams around functionality
Don’t separate designers from coders, testers from data modelers. Build teams the way you build code. -
Don’t use manual procedures
A shell script or batch file will execute the same instructions, in the same order, time after time. -
Test early. Test often. Test automatically
Tests that run with every build are much more effective than test plans that sit on a shelf. -
Coding ain’t done ’til all the tests run
‘Nuff said. -
Use saboteurs to test your testing
Introduce bugs on purpose in a separate copy of the source to verify that testing will catch them. -
Test state coverage, not code coverage
Identify and test significant program states. Just testing lines of code isn’t enough. -
Find bugs once
Once a human tester finds a bug, it should be the last time a human tester finds that bug. Automatic tests should check for it from then on. -
English is just a programming language
Write documents as you would write code: honor the DRY principle, use metadata, MVC, automatic generation, and so on. -
Build documentation in, don’t bolt it on
Documentation created separately from code is less likely to be correct and up to date. -
Gently exceed your users’ expectations
Come to understand your users’ expectations, then deliver just that little bit more. -
Sign your work
Craftsmen of an earlier age were proud to sign their work. You should be, too.