seven ineffective coding habits of many programmers

Post on 13-Jan-2017

591 Views

Category:

Software

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Seven Ineffective

Coding Habits of

Many Programmers

@KevlinHenney

/ WordFriday

code, noun

a set of instructions for a computer

a computer program, or a portion thereof

a system of words, figures or symbols used to

represent others, especially for the purposes of

secrecy

a set of conventions or principles governing

behaviour or activity in a particular domain

Concise Oxford English Dictionary ∙ Oxford English Dictionary ∙ Merriam-Webster's Collegiate Dictionary

habit, noun

a settled or regular tendency or practice

an acquired mode of behaviour that has become

nearly or completely involuntary

bodily condition or constitution

a costume characteristic of a calling, rank, or

function

Concise Oxford English Dictionary ∙ Oxford English Dictionary ∙ Merriam-Webster's Collegiate Dictionary

As a

I want

So that

$Role

$Feature

$Benefit

As a

I want

So that

programmer

$Feature

$Benefit

As a

I want

So that

programmer

effective coding habits

$Benefit

As a

I want

So that

programmer

effective coding habits

I can be less ineffective

As a

I want

So that

programmer

effective coding habits

I can be more effective

As a

I want

So that

programmer

effective coding habits

I can spend less time determining the meaning of code and more time coding meaningfully

As a

I want

So that

programmer

???

I can spend less time determining the meaning of code and more time coding meaningfully

Noisy Code

Signal-to-noise ratio (often abbreviated SNR or

S/N) is a measure used in science and engineering

that compares the level of a desired signal to the

level of background noise.

Signal-to-noise ratio is sometimes used informally

to refer to the ratio of useful information to false or

irrelevant data in a conversation or exchange.

http://en.wikipedia.org/wiki/Signal_to_noise_ratio

To be, or not to be: that is the question:

Whether 'tis nobler in the mind to suffer

The slings and arrows of outrageous fortune,

Or to take arms against a sea of troubles,

And by opposing end them?

William Shakespeare

Hamlet

Continuing existence or cessation of

existence: those are the scenarios. Is it

more empowering mentally to work towards

an accommodation of the downsizings and

negative outcomes of adversarial

circumstance, or would it be a greater

enhancement of the bottom line to move

forwards to a challenge to our current

difficulties, and, by making a commitment

to opposition, to effect their demise?

Tom Burton

Long Words Bother Me

function leftpad (str, len, ch) { str = String(str); var i = -1; if (!ch && ch !== 0) ch = ' '; len = len - str.length; while (++i < len) { str = ch + str; } return str; }

http://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/

function leftpad(content, length, pad) { content = String(content) pad = String(pad || pad === 0 ? pad : ' ')[0] var left = Math.max(length - content.length, 0) return pad.repeat(left) + content }

var cache = [ '', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' ]; function leftPad (str, len, ch) { // convert `str` to `string` str = str + ''; // `len` is the `pad`'s length now len = len - str.length; // doesn't need to pad if (len <= 0) return str; // `ch` defaults to `' '` if (!ch && ch !== 0) ch = ' '; // convert `ch` to `string` ch = ch + ''; // cache common use cases if (ch === ' ' && len < 10) return cache[len] + str; // `pad` starts with an empty string var pad = ''; // loop while (true) { // add `ch` to `pad` if `len` is odd if (len & 1) pad += ch; // divide `len` by 2, ditch the remainder len >>= 1; // "double" the `ch` so this operation count grows logarithmically on `len` // each time `ch` is "doubled", the `len` would need to be "doubled" too // similar to finding a value in binary search tree, hence O(log(n)) if (len) ch += ch; // `len` is 0, exit the loop else break; } // pad `str`! return pad + str; }

Comments

A delicate matter, requiring taste and judgement. I tend to err on the side of eliminating comments, for several reasons. First, if the code is clear, and uses good type names and variable names, it should explain itself. Second, comments aren't checked by the compiler, so there is no guarantee they're right, especially after the code is modified. A misleading comment can be very confusing. Third, the issue of typography: comments clutter code.

Rob Pike, "Notes on Programming in C"

There is a famously bad comment style:

i=i+1; /* Add one to i */

and there are worse ways to do it:

/**********************************

* *

* Add one to i *

* *

**********************************/

i=i+1;

Don't laugh now, wait until you see it in real life.

Rob Pike, "Notes on Programming in C"

A common fallacy is to assume authors of incomprehensible code will somehow be able to express themselves lucidly and clearly in comments.

Kevlin Henney https://twitter.com/KevlinHenney/status/381021802941906944

Unsustainable Spacing

To be, or not to be: that is the question:

Whether 'tis nobler in the mind to suffer

The slings and arrows of outrageous fortune,

Or to take arms against a sea of troubles,

And by opposing end them?

William Shakespeare

Hamlet

Continuing existence or cessation of

existence: those are the scenarios. Is it

more empowering mentally to work towards

an accommodation of the downsizings and

negative outcomes of adversarial

circumstance, or would it be a greater

enhancement of the bottom line to move

forwards to a challenge to our current

difficulties, and, by making a commitment

to opposition, to effect their demise?

Tom Burton

Long Words Bother Me

Continuing existence or cessation of existence:

those are the scenarios. Is it

more empowering mentally

to work towards an

accommodation of the

downsizings and negative

outcomes of adversarial

circumstance, or would it be

a greater enhancement of

the bottom line to move

forwards to a challenge to

our current difficulties, and,

by making a commitment to

opposition, to effect their

demise?

How many programmers lay out their code

Column 80

How people read

To answer the question "What is clean design?"

most succinctly: a clean design is one that

supports visual thinking so people can meet their

informational needs with a minimum of

conscious effort.

Daniel Higginbotham ∙ "Clean Up Your Mess — A Guide to Visual Design for Everyone" ∙ http://www.visualmess.com/

You convey information by the way you arrange

a design's elements in relation to each other. This

information is understood immediately, if not

consciously, by the people viewing your designs.

Daniel Higginbotham ∙ "Clean Up Your Mess — A Guide to Visual Design for Everyone" ∙ http://www.visualmess.com/

This is great if the visual relationships are

obvious and accurate, but if they're not, your

audience is going to get confused. They'll have to

examine your work carefully, going back and

forth between the different parts to make sure

they understand.

Daniel Higginbotham ∙ "Clean Up Your Mess — A Guide to Visual Design for Everyone" ∙ http://www.visualmess.com/

public int howNotToLayoutAMethodHeader(int firstArgument,

String secondArgument)

public int ensureArgumentsAreAlignedLikeThis(

int firstArgument,

String secondArgument)

public int orEnsureArgumentsAreGroupedLikeThis(

int firstArgument, String secondArgument)

public int butNotAlignedLikeThis(int firstArgument,

String secondArgument)

int doNotFormat = likeThis(someArgumentOrExpression,

anotherArgumentOrExpression);

int insteadFormat =

somethingLikeThis(

someArgumentOrExpression,

anotherArgumentOrExpression);

int orFormat = somethingLikeThis(

someArgumentOrExpression,

anotherArgumentOrExpression);

int asItIs = unstable(someArgumentOrExpression,

anotherArgumentOrExpression);

int butThisIs =

stable(

someArgumentOrExpression,

anotherArgumentOrExpression);

int andThisIs = stable(

someArgumentOrExpression,

anotherArgumentOrExpression);

public ResultType arbitraryMethodName(FirstArgumentType firstArgument,

SecondArgumentType secondArgument,

ThirdArgumentType thirdArgument) {

LocalVariableType localVariable = method(firstArgument,

secondArgument);

if (localVariable.isSomething(thirdArgument,

SOME_SHOUTY_CONSTANT)) {

doSomethingWith(localVariable);

}

return localVariable.getSomething();

}

public ResultType arbitraryMethodName(

FirstArgumentType firstArgument,

SecondArgumentType secondArgument,

ThirdArgumentType thirdArgument) {

LocalVariableType localVariable =

method(firstArgument, secondArgument);

if (localVariable.isSomething(

thirdArgument, SOME_SHOUTY_CONSTANT)) {

doSomething(localVariable);

}

return localVariable.getSomething();

}

XXXXXX XXXXXXXXXX XXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXX XXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXX XXXXXXXXXXXXX

XXXXXXXXXXXXXXXXX XXXXXXXXXXXXX

XXXXXX XXXXXXXXXXXXX XXXXXXXXXXXXXX

XX XXXXXXXXXXXXX XXXXXXXXXXX

XXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXX XXXXXXXXXXXXX

XXXXXX XXXXXXXXXXXXX XXXXXXXXXXXX

public ResultType arbitraryMethodName(

FirstArgumentType firstArgument,

SecondArgumentType secondArgument,

ThirdArgumentType thirdArgument) {

LocalVariableType localVariable =

method(firstArgument, secondArgument);

if (localVariable.isSomething(

thirdArgument, SOME_SHOUTY_CONSTANT)) {

doSomething(localVariable);

}

return localVariable.getSomething();

}

XXXXXX XXXXXXXXXX XXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXX XXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXX XXXXXXXXXXXXX

XXXXXXXXXXXXXXXXX XXXXXXXXXXXXX

XXXXXX XXXXXXXXXXXXX XXXXXXXXXXXXXX

XX XXXXXXXXXXXXX XXXXXXXXXXX

XXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXX XXXXXXXXXXXXX

XXXXXX XXXXXXXXXXXXX XXXXXXXXXXXX

public ResultType arbitraryMethodName(

FirstArgumentType firstArgument,

SecondArgumentType secondArgument,

ThirdArgumentType thirdArgument)

{

LocalVariableType localVariable =

method(firstArgument, secondArgument);

if (localVariable.isSomething(

thirdArgument, SOME_SHOUTY_CONSTANT))

{

doSomething(localVariable);

}

return localVariable.getSomething();

}

XXXXXX XXXXXXXXXX XXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXX XXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXX XXXXXXXXXXXXX

XXXXXXXXXXXXXXXXX XXXXXXXXXXXXX

XXXXXX XXXXXXXXXXXXX XXXXXXXXXXXXXX

XX XXXXXXXXXXXXX XXXXXXXXXXX

XXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXX XXXXXXXXXXXXX

XXXXXX XXXXXXXXXXXXX XXXXXXXXXXXX

Lego Naming

Agglutination is a process in linguistic morphology

derivation in which complex words are formed by

stringing together morphemes, each with a single

grammatical or semantic meaning.

Languages that use agglutination widely are called

agglutinative languages.

http://en.wikipedia.org/wiki/Agglutination

pneumonoultramicroscopicsilicovolcanoconiosis

Rindfleischetikettierungsüberwachungsaufgabenübertragungsgesetz

fylkestrafikksikkerhetsutvalgssekretariatslederfunksjonene

muvaffakiyetsizleştiricileştiriveremeyebileceklerimizdenmişsinizcesine

hippopotomonstrosesquipedaliophobia

http://www.bonkersworld.net/object-world/

http://www.bonkersworld.net/object-world/

OBJECT-ORIENTED

VenetianBlind Door

Television

Picture Glass

Sofa TelevisionRemoteControl

Peephole

People will be using the

words you choose in their

conversation for the next 20

years. You want to be sure

you do it right.

Unfortunately, many people

get all formal [...]. Just calling

it what it is isn't enough.

public interface ConditionChecker { boolean checkCondition(); ... }

public interface Condition { boolean isTrue(); ... }

They have to tack on a

flowery, computer science-y,

impressive sounding, but

ultimately meaningless word,

like Object, Thing,

Component, Part, Manager,

Entity, or Item.

AccessViolationException

ArgumentOutOfRangeException

ArrayTypeMismatchException

BadImageFormatException

CannotUnloadAppDomainException

EntryPointNotFoundException

IndexOutOfRangeException

InvalidOperationException

OverflowException

AccessViolation

ArgumentOutOfRange

ArrayTypeMismatch

BadImageFormat

CannotUnloadAppDomain

EntryPointNotFound

IndexOutOfRange

InvalidOperation

Overflow

ArgumentException

ArithmeticException

ContextMarshalException

FieldAccessException

FormatException

NullReferenceException

ObjectDisposedException

RankException

TypeAccessException

Argument

Arithmetic

ContextMarshal

FieldAccess

Format

NullReference

ObjectDisposed

Rank

TypeAccess

InvalidArgument

InvalidArithmeticOperation

FailedContextMarshal

InvalidFieldAccess

InvalidFormat

NullDereferenced

OperationOnDisposedObject

ArrayRankMismatch

InvalidTypeAccess

Omit needless words.

William Strunk and E B White

The Elements of Style

Underabstraction

http://fragmental.tw/2009/04/29/tag-clouds-see-how-noisy-your-code-is/

http://fragmental.tw/2009/04/29/tag-clouds-see-how-noisy-your-code-is/

if (portfolioIdsByTraderId.get(trader.getId()) .containsKey(portfolio.getId())) { ... }

Dan North, "Code in the Language of the Domain" 97 Things Every Programmer Should Know

if (trader.canView(portfolio)) { ... }

Dan North, "Code in the Language of the Domain" 97 Things Every Programmer Should Know

Unencapsulated State

public class BankAccount { ... public decimal Balance; ... }

public class BankAccount { ... public decimal Balance { get; set; } ... }

public class BankAccount { ... public decimal Balance { get ... set ... } ... }

Don't ever invite a

vampire into your

house, you silly boy.

It renders you

powerless.

public class BankAccount { ... public decimal Balance { get ... } public void Deposit(decimal amount) ... public void Withdraw(decimal amount) ... ... }

Getters and Setters

public class Money implements ... { ... public int getUnits() ... public int getHundredths() ... public Currency getCurrency() ... ... public void setUnits(int newUnits) ... public void setHundredths(int newHundredths) ... public void setCurrency(Currency newCurrency) ... ... }

public final class Money implements ... { ... public int getUnits() ... public int getHundredths() ... public Currency getCurrency() ... ... }

Just because you have a getter, doesn't mean you should have a setter.

public final class Money implements ... { ... public int getUnits() ... public int getHundredths() ... public Currency getCurrency() ... ... }

public final class Money implements ... { ... public int units() ... public int hundredths() ... public Currency currency() ... ... }

"Get something" is an imperative with an expected side effect.

Uncohesive Tests

Everybody knows that TDD stands for Test Driven Development. However, people too often concentrate on the words "Test" and "Development" and don't consider what the word "Driven" really implies.

For tests to drive development they must do more than just test that code performs its required functionality: they must clearly express that required functionality to the reader.

That is, they must be clear specifications of the required functionality. Tests that are not written with their role as specifications in mind can be very confusing to read.

Nat Pryce and Steve Freeman "Are Your Tests Really Driving Your Development?"

Stack<Book>

public class Stack<T> { private public Stack() public void push(T newTop) public void pop() public int depth() public T top() }

public class StackTests { @Test public void testConstructor() @Test public void testPush() @Test public void testPop() @Test public void testDepth() @Test public void testTop() }

public class StackTests { @Test public void constructor() @Test public void push() @Test public void pop() @Test public void depth() @Test public void top() }

method test

test

test

method

method

test

test

public class Stack_spec { public static class A_new_stack { @Test public void is_empty() }

public static class An_empty_stack { @Test() public void throws_when_queried_for_its_top_item() @Test() public void throws_when_popped() @Test public void acquires_depth_by_retaining_a_pushed_item_as_its_top() }

public static class A_non_empty_stack { @Test public void becomes_deeper_by_retaining_a_pushed_item_as_its_top() @Test public void on_popping_reveals_tops_in_reverse_order_of_pushing() } }

public class Stack_spec { public static class A_new_stack { @Test public void is_empty() }

public static class

An_empty_stack { @Test() public void throws_when_queried_for_its_top_item() @Test() public void throws_when_popped() @Test public void acquires_depth_by_retaining_a_pushed_item_as_its_top() }

public static class

A_non_empty_stack { @Test public void becomes_deeper_by_retaining_a_pushed_item_as_its_top() @Test public void on_popping_reveals_tops_in_reverse_order_of_pushing() } }

public class Stack_spec { public static class A_new_stack { @Test public void is_empty() }

public static class

An_empty_stack { @Test() public void throws_when_queried_for_its_top_item() @Test() public void throws_when_popped() @Test public void acquires_depth_by_retaining_a_pushed_item_as_its_top() }

public static class

A_non_empty_stack { @Test public void becomes_deeper_by_retaining_a_pushed_item_as_its_top() @Test public void on_popping_reveals_tops_in_reverse_order_of_pushing() } }

function leftpad (str, len, ch) { str = String(str); var i = -1; if (!ch && ch !== 0) ch = ' '; len = len - str.length; while (++i < len) { str = ch + str; } return str; }

function leftpad(content, length, pad) { content = String(content) pad = String(pad || pad === 0 ? pad : ' ')[0] var left = Math.max(length - content.length, 0) return pad.repeat(left) + content }

function test

test

test

function assert(condition) { if(!condition) throw { name: "AssertionError", message: "assertion failed" } }

function testPasses(toTry) { try { toTry() return true } catch (failure) { return false } }

function report(testName, passed) { document.write(testName.fontcolor(passed ? "green" : "red") + "<br>") }

function test(testCases) { for (var testName in testCases) if (testCases.hasOwnProperty(testName)) report(testName, testPasses(testCases[testName])) }

test({ "Padding an empty string to a length of 0 results in an empty string": () => assert(leftpad("", 0, "X") === ""), "Padding a non-empty string to a shorter length results in the same string": () => assert(leftpad("foobar", 3, "X") === "foobar"), "Padding a non-empty string to a negative length results in the same string": () => assert(leftpad("foobar", -3, "X") === "foobar"), "Padding a non-empty string to its length results in the same string": () => assert(leftpad("foobar", 6, "X") === "foobar"), "Padding to a longer length with a single character fills to the left": () => assert(leftpad("foobar", 8, "X") === "XXfoobar"), "Padding to a longer length with surplus characters fills using only first": () => assert(leftpad("foobar", 10, "XY") === "XXXXfoobar"), "Padding to a longer length with an empty string fills with space": () => assert(leftpad("foobar", 8, "") === " foobar"), "Padding to a longer length with no specified fill fills with space": () => assert(leftpad("foobar", 9) === " foobar"), "Padding to a longer length with integer 0 fills with 0": () => assert(leftpad("foobar", 7, 0) === "0foobar"), "Padding to a longer length with single-digit integer fills with digit": () => assert(leftpad("foobar", 10, 1) === "1111foobar"), "Padding to a longer length with multiple-digit integer fills with first digit": () => assert(leftpad("foobar", 10, 42) === "4444foobar"), "Padding to a longer length with negative integer fills with -": () => assert(leftpad("foobar", 8, -42) === "--foobar"), "Padding a non-string uses string representation": () => assert(leftpad(4.2, 5, 0) === "004.2") })

Padding an empty string to a length of 0 results in an empty string

Padding a non-empty string to a shorter length results in the same string

Padding a non-empty string to a negative length results in the same string

Padding a non-empty string to its length results in the same string

Padding to a longer length with a single character fills to the left

Padding to a longer length with surplus characters fills using only first

Padding to a longer length with an empty string fills with space

Padding to a longer length with no specified fill fills with space

Padding to a longer length with integer 0 fills with 0

Padding to a longer length with single-digit integer fills with digit

Padding to a longer length with multiple-digit integer fills with first digit

Padding to a longer length with negative integer fills with -

Padding a non-string uses string representation

Padding an empty string to a length of 0 results in an empty string

Padding a non-empty string to a shorter length results in the same string

Padding a non-empty string to a negative length results in the same string

Padding a non-empty string to its length results in the same string

Padding to a longer length with a single character fills to the left

Padding to a longer length with surplus characters fills using only first

Padding to a longer length with an empty string fills with space

Padding to a longer length with no specified fill fills with space

Padding to a longer length with integer 0 fills with 0

Padding to a longer length with single-digit integer fills with digit

Padding to a longer length with multiple-digit integer fills with first digit

Padding to a longer length with negative integer fills with -

Padding a non-string uses string representation

A test case should be just that: it should correspond to a single case.

As a

I want

So that

programmer

???

I can spend less time determining the meaning of code and more time coding meaningfully

As a

I want

So that

programmer

code to communicate directly and with intent

I can spend less time determining the meaning of code and more time coding meaningfully

At some level

the style

becomes the

substance.

top related