11 rules for writing better code

opinion
Feb 26, 20258 mins

Complexity makes misery. Folow these hard-earned and time-tested guidelines to keep your code simple, clear, and easy to maintain.

Spaghetti Incident 16z9
Credit: David Calvert - shutterstock.com

You canโ€™t develop software and manage software projects for 30 years and not learn a thing or two about writing good code. Here are a near-dozen nuggets of coding wisdom I picked up along the way.

Simpler is better

This basic idea almost goes without saying, but I am frequently surprised at how often the KISS principleย (โ€œKeep it simple, stupidโ€) is ignored.ย Developers seem to love newfangled ways of doing things.ย Why do a โ€œsimple create and use an objectโ€ when you can pass around lambdas to do all the work?ย Why use a simple, standard data structure when you can build your own, custom, cutting-edge solution? Whatโ€™s another layer of abstraction among friends?

Just because that new language feature is cool and complex doesnโ€™t mean you have to use it.ย Complexity is the opposite of simplicity, and the way to avoid complex code is to never write anything complex.ย Only write simple things.

Be clear

I am astonished at how often developers will unintentionally obfuscate their code.ย Just because you know what the variable named TxMgrObj means doesnโ€™t mean the next developer does. Why not just type out TransactionManager?ย 

This hurts my eyes:


const txMgrObj = getTxMgr();
txMgrObj.process();

But I find this pleasing:


const transactionManager = getTransactionManager();
transactionManager.process();

And if you say to me โ€œThatโ€™s too much typing,โ€ Iโ€™ll respond with โ€œLazy is no way to go through life, my friend.โ€ย Thinking that saving a few keystrokes is going to save you time and effort is foolish and ridiculous.ย 

Being clear often means more lines of code. For instance, always use an explaining variable. Instead of an if statement with four different boolean conditions, how about four boolean variables, and then a fifth that defines the relationship between the first four, and use that in the if statement? (Or better yet, avoid the if statement entirelyโ€”see below.)

Sure, naming things is hard, but a nice long name can explain a lot.ย Donโ€™t be afraid to have a variable named NumberOfSpacesAllowedOnaPrinterLine.ย Trust me, no one will complain.

Follow the Law of Demeter

When you go up to the checkout station at the grocery store, you donโ€™t hand over your whole wallet to pay.ย You pull out your card.ย 

The Law of Demeter states that you should limit interactions to the absolute minimum.ย If your method requires a transaction number, pass in the transaction number. Donโ€™t pass in a class that has the transaction number in it, or a query object that contains the transaction number.ย 

Code for 0, 1, or N

I worked on a system with a tax array that was hard-coded to six and no more than six elements.ย The whole system was designed with that limitation.ย Iโ€™m sure that when the code was written the developer thought that six different taxes for an order was an absurdly large number and made it six โ€œjust in case.โ€ Well, do I even need to tell you what happened?

Sometimes you need to make sure that there are zero things in an order before deleting it.ย Sometimes you need to ensure that there is only one event handler for a given event.ย Sometimes you need to allow any number of event handlers.ย 

And you need to be very, very sure before you decide that there can be no more than three of anything in your system.ย A corollary to this rule isโ€ฆ

Donโ€™t hard-code anything

This seems obvious, but some developers love to hard-code stuff. Sweet baby Elvis, I even see this kind of thing all the time:


someString.PadLeft(13);

I mean, really?ย Why 13?ย Why not 14?ย Or 12? How about a constant that explains the meaning of the value?

You may not think so, but every time you create an object inside a class, you are hard-coding that class and its implementation. For example:


class SimpleEncryptor {
ย  public encrypt(plainText: string): string {
ย ย ย  const weakEncryption = new WeakEncryptionAlgorithm();
ย ย ย  return weakEncryption.encrypt(plainText);
ย  }
}

So what if you want to change the encryption algorithm?ย 

Instead, use dependency injection, and you can use any algorithm you want:


interface IEncryptionAlgorithm {
ย  encrypt(plainText: string): string;
}

class SimpleEncryptor {
ย  public encrypt(plainText: string, encryptionAlgorithm: IEncryptionAlgorithm): string {
ย ย ย  return encryptionAlgorithm.encrypt(plainText);
ย  }
}

Often โ€˜over-engineeringโ€™ is proper engineering

Believe me, I get the notion of over-engineering.ย I just told you to keep things simple.ย But sometimes, doing things โ€œthe right wayโ€ looks like over-engineering. Whenever you think you are over-engineering, stop and consider that, well, maybe you arenโ€™t.ย Creating interfaces and coding against them can seem like over-engineering. I know itโ€™s a fine line to walk, but planning ahead for something you know you will need is not wrong. And this leads me toโ€ฆ

Sometimes you are going to need it

Iโ€™ve never quite understood the YAGNI principle (โ€œYou arenโ€™t gonna need itโ€).ย All too often, you find that, well, you know, you did end up needing it.ย And by then, implementing this thing you โ€œwerenโ€™t going to needโ€ has become such a nightmare that you dearly wish you had gone ahead an laid the groundwork for it.ย 

Maybe you hard-coded something (you werenโ€™t going to need flexibility here, right?). Maybe you didnโ€™t plan on ever needing seven taxes, or a different encryption algorithm.ย I see no harm in thinking โ€œYou know, eventually, we are going to need to deal with more than widgets hereโ€ and coding so that changes are easy when new cogs and sprockets inevitably come along.

Make the command line your first user interface

All of your business logic and other, non-interface code should be accessible via a command-line application. If you canโ€™t run your code at the command line, and it requires some kind of GUI or other interface to run, then you have improperly coupled your code and possibly tied it in knots.

This is an approach that I started taking when I realized that at the heart of all crappy code is the tying of business logic to the user interface.ย The two should be treated like oil and vinegar, but all too often they are mixed together like Kool-Aid. Separate your concerns and prove it by running your code with a command-line application.

Be wary of if statements (very wary)

I am a firm believer that every use of an if statement should cause the humble developer to stop and think โ€œShould this be two routines?โ€ Or better, โ€œShould this be a whole new class?โ€

The vast majority of awful spaghetti code and impossibly huge routines that I see are the result of a massive number of deeply embedded if statements. You know what Iโ€™m talking about. Itโ€™s the routine that literally handles 47 different situations, all in one giant, thousands-of-lines-long procedure.

Be very wary of if statements, because they often lead to violations of the final lesson, which isโ€ฆ

Everything should do one and only one thing

This might be the single most important rule a software developer should know.ย Everything should do one thing.ย Everything.ย Every line of code.ย Every routine.ย  Every class. Everything.

As Ryan Singer has said, there is no end to the misery caused by trying to make one thing do two things.ย Just think of the awfulness that is having one thing do 26 things.ย 

Every time I see a procedure name with โ€œandโ€ in it, I want to scream.ย ProcessAndStoreOrder()? No!

If you see an opportunity to use Extract Method refactoring, do it.ย Do you ever wonder โ€œWhat is the longest I should let my procedures be?โ€ The answer is simple:ย Keep making them shorter with your Extract Method tool until you canโ€™t apply that refactoring anymore.ย The code will quickly become a collection of discreet methods that do only one thing.

Sure, youโ€™ll end up with countless little functions and procedures. But of course, what is encapsulation other than gathering functions, procedures, and data together?ย 

Complexity hurts

If there is one rule that sums it all up, itโ€™s that complexity makes you miserable.ย Writing good code means writing simple, clear, โ€œboringโ€ codeโ€”code that minimizes the cognitive effort required to understand it.ย 

And if there is one last thought you walk away with here, let it be this advice from John Woods:ย โ€œAlways code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.โ€

Nick Hodges

Nick has a BA in classical languages from Carleton College and an MS in information technology management from the Naval Postgraduate School. In his career, he has been a busboy, a cook, a caddie, a telemarketer (for which he apologizes), an office manager, a high school teacher, a naval intelligence officer, a software developer, a product manager, and a software development manager. In addition, he is a former Delphi Product Manager and Delphi R&D Team Manager and the author of Coding in Delphi. He is a passionate Minnesota sports fanโ€”especially the Timberwolvesโ€”as he grew up and went to college in the Land of 10,000 Lakes. He currently lives in West Chester, PA.

More from this author