Software Engineering Stack Exchange is a question and answer site for professionals, academics, and students working within the systems development life cycle who care about creating, delivering, and maintaining software responsibly. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have read a book called Clean Code by Robert C. Martin. In this book I've seen many methods to clean up the code like writing small functions, choosing names carefully, etc. It seems by far the most interesting book about clean code I've read. However, today my boss didn't like the way I wrote code after reading this book.

His arguments were

  • Writing small functions is a pain because it forces you to move into each small functions to see what the code is doing
  • Put everything in a main big loop even if the main loop is more than 300 lines, it is faster to read
  • Write only small functions if you have to duplicate code
  • Don't write a function with the name of the comment, put your complex line of code (3-4 lines) with a comment above; similarily you can modify the failing code directly

This is against everything I've read. How do you usually write code? One main big loop, no small functions?

The language I use is mainly Javascript. I really have difficulties reading now since I've deleted all my small clearly named functions and put everything in a big loop. However, my boss likes it this way.

One example was:

// The way I would write it
if (isApplicationInProduction(headers)) {
  phoneNumber = headers.resourceId;
} else {
  phoneNumber = DEV_PHONE_NUMBER;
}

function isApplicationInProduction(headers) {
   return _.has(headers, 'resourceId');
}

// The way he would write it
// Take the right resourceId if application is in production
phoneNumber = headers.resourceId ? headers.resourceId : DEV_PHONE_NUMBER;

In the book I've read for example comments are considered as failure to write clean code because they are obsolete if you write small functions and often leads to non updated comment (you modify your code and not the comment). However what I do is delete the comment and write a function with the name of the comment.

Well, I would like some advice, which way/practice is better to write clean code?

share|improve this question
30  
Self-documenting code may be good, but notice that your version uses 6× more code! “Keep it simple” should be kept in mind. And neither version addresses the actual source of complexity in that piece of code: the conditional. If you extract a function, it should be phoneNumer = getPhoneNumber(headers), thus simplifying the main code, and only increasing the amount of code by 3×. – amon 14 hours ago
17  
When reading most of the books targeted at this industry, never drink too much of the authors' Kool Aid. – Blrfl 14 hours ago
21  
If the one line of code is clear and easy to understand - why do you need to split it into N lines? I would agree with you boss regarding the code in your question – Valery 14 hours ago
8  
if isApplicationInProduction isn't used anywhere else, then it's not worth making it into a function as isn't complicated by itself. – whatsisname 13 hours ago
8  
_.has(headers, 'resourceId'); looks to me like a very unreliable test for whether the application is running in production! – immibis 7 hours ago
up vote 34 down vote accepted

Taking the code examples first. You favour:

if (isApplicationInProduction(headers)) {
  phoneNumber = headers.resourceId;
} else {
  phoneNumber = DEV_PHONE_NUMBER;
}

function isApplicationInProduction(headers) {
   return _.has(headers, 'resourceId');
}

And your boss would write it as:

// Take the right resourceId if application is in production
phoneNumber = headers.resourceId ? headers.resourceId : DEV_PHONE_NUMBER;

In my view, both have problems. As I read your code, my immediate thought was "you can replace that if with a tenery expression". Then I read your boss' code and thought "why's he replaced your function with a comment?".

I'd suggest the optimal code is between the two:

phoneNumber = isApplicationInProduction(headers) ? headers.resourceId : DEV_PHONE_NUMBER;

function isApplicationInProduction(headers) {
   return _.has(headers, 'resourceId');
}

That gives you the best of both worlds: a simplified test expression and the comment is replaced with testable code.

Regarding your boss' views on code design though:

Writing small functions is a pain because it forces you to move into each small functions to see what the code is doing.

If the function is well-named, this isn't the case. isApplicationInProduction is self-evident and it should not be necessary to examine the code to see what it does. In fact the opposite is true: examining the code reveals less as to the intention than the function name does (which is why your boss has to resort to comments).

Put everything in a main big loop even if the main loop is more than 300 lines, it is faster to read

It may be faster to scan through, but to truly "read" the code, you need to be able to effectively execute it in your head. That's easy with small functions and is really, really hard with methods that are 100's of lines long.

Write only small functions if you have to duplicate code

I disagree. As your code example shows, small, well-named functions improve readability of code and should be used whenever eg you aren't interested in the "how", only the "what" of a piece of functionality.

Don't write a function with the name of the comment, put your complex line of code (3-4 lines) with a comment above. Like this you can modify the failing code directly

I really can't understand the reasoning behind this one, assuming it really is serious. It's the sort of thing I'd expect to see written in parody by The Expert Beginner twitter account. Comments have a fundamental flaw: they aren't compiled/interpreted and so can't be unit tested. The code gets modified and the comment gets left alone and you end up not knowing which is right.

Writing self-documenting code is hard, and supplementary docs (even in the form of comments) are sometimes needed. But "Uncle Bob"'s view that comments are a coding failure holds true all too often.

Get your boss to read the Clean Code book and try to resist making your code less readable just to satisfy him. Ultimately though, if you can't persuade him to change, you have to either fall in line or find a new boss that can code better.

share
17  
disagree.. creating a method to to do something so simple is useless unless it is being reused all over the place. – Matthew Whited 11 hours ago
5  
it's clear what it is doing. if you are concerned then add a comment to say what it is doing not how or why. – Matthew Whited 11 hours ago
5  
@MatthewWhited, "f you are concerned then add a comment to say what it is doing". As I say in my answer, I find it utterly incomprehensible why anyone would ever consider replacing a function name, that describes what the code does, with inline code accompanied by a comment that descries what the code does. – David Arno 10 hours ago
6  
@ThomasKilian, "A loop should fit one screen page". What?!?? Not in my world, mate. I aim for the whole file to fit on the screen, and set myself a non-negotiable (except in real edge-case situations) 200 line limit to files and 20 lines for functions. Never looked back since doing that. – David Arno 10 hours ago
7  
Guess what – Mathias Ettinger 9 hours ago

there are other problems

Neither code is good, because both basically bloat the code with a debug test case. What if you want to test more things for whatever reason?

phoneNumber = DEV_PHONE_NUMBER_WHICH_CAUSED_PROBLEMS_FOR_CUSTOMERS;

or

phoneNumber = DEV_PHONE_NUMBER_FROM_OTHER_COUNTRY;

Do you want to add even more branches?

The significant problem is that you basically duplicate part of your code and thus you aren't actually testing the real code. You write debug code to test the debug code, but not the production code. It's like partially creating a parallel codebase.

You are arguing with your boss about how to write bad code more cleverly. Instead, you should fix the inherent problem of the code itself.

dependency injection

This is how your code should look like:

phoneNumber = headers.resourceId;

There's no branching here, because the logic here doesn't have any branching. The program should pull the phone number from the header. Period.

If you want to have DEV_PHONE_NUMBER_FROM_OTHER_COUNTRY as a result, you should put it into headers.resourceId. One way of doing it is to simply inject a different headers object for test cases (sorry if this is not proper code, my javascript skills are a bit rusty):

function foo(headers){
    phoneNumber = headers.resourceId;
}

// creating the test case
foo({resourceId: DEV_PHONE_NUMBER_FROM_OTHER_COUNTRY});

Assuming that headers is part of a response you receive from a server: Ideally you have a whole test server that delivers headers of various kinds for testing purposes. This way you test the actual production code as-is and not some half duplicated code that may or may not work like the production code.

share|improve this answer
1  
I did consider addressing this very topic in my own answer, but felt it was already long enough. So +1 to you for doing so :) – David Arno 10 hours ago
2  
@DavidArno I was about to add it as a comment to your answer, because the question was still locked when I first read it, found to my surprise that it was open again and so added this as an answer. Maybe it should be added that there are dozens of frameworks/tools for doing automated testing. Especially in JS there seems to be a new one coming out every day. Might be hard to sell that to the boss though. – null 10 hours ago
1  
Thank you. This seems like the best way to do it in fact ! – TivBroc 9 hours ago
    
Best answer, upvoted. Though, if their test system is "hard to inject", I wouldn't be adverse to a temp practical fix: phoneNumber = headers.resourceId | TEMP_DEBUG_PHONE_FIXME; to clarify the intent that this code must be cleaned up before release. – user949300 6 hours ago

There is no "right" or "wrong" answer to this. However, I will offer my opinion based on 36 years of professional experience designing and developing software systems ...

  1. There is no such thing as "self-documenting code." Why? Because that claim in completely subjective.
  2. Comments are never failures. What is a failure is code that cannot be understood at all without comments.
  3. 300 uninterrupted lines of code in one code block is a maintenance nightmare and highly prone to errors. Such a block is strongly indicative of bad design and planning.

Speaking directly to the example you provided ... Placing isApplicationInProduction() into its own routine is the smart(er) thing to do. Today that test is simply a check of "headers" and can be handled in a ternary (?:) operator. Tomorrow, the test may be far more complex. Additionally, "headers.resourceId" has no clear relation to the application's "in production status;" I would argue that a test for such status needs to be decoupled from the underlying data; a subroutine will do this and a ternary will not. Additionally, a helpful comment would by why resourceId is a test for "in production."

Be careful not to go overboard with "small clearly named functions." A routine should encapsulate an idea more so than "just code." I support amon's suggestion of phoneNumber = getPhoneNumber(headers) and add that getPhoneNumber() should do the "production status" test with isApplicationInProduction()

share|improve this answer
5  
There is such a thing as good comments which are not a failure. However, comments that are nearly verbatim the code they supposedly explain or are just empty comment blocks preceding a method/class/etc. are definitely a failure. – jpmc26 10 hours ago
    
It's possible to have code which is smaller and easier to read than any English-language description of what it does and the corner cases it does and does not handle. Further, if a function is pulled out into its own method, someone reading the function won't know what corner cases are or are not handled by its callers, and unless the function's name is very verbose someone examining the callers may not know what corner cases are handled by the function. – supercat 8 hours ago
    
"code that cannot be understood at all without comments" - should this be parsed as "code that cannot (be understood at all without comments)" or "(code that cannot be understood at all) without comments"? They have almost opposite meanings. – immibis 7 hours ago
    
Comments are never intrinsically failures. Comments can be failures, and are so when they are inaccurate. Wrong code can be detected at multiple levels, including by wrong behavior in black box mode. Wrong comments can only be detected by human comprehension in white box mode, through recognition that two models are described and that one of them is incorrect. – Timbo 6 hours ago
    
@Timbo You mean, "... at least one of them is incorrect." ;) – jpmc26 4 hours ago

Robert Martin's programming style is polarizing. You will find many programmers, even experienced ones, who find a lot of excuses why splitting up "that much" is too much, and why keeping functions a little bit bigger is "the better way". However, most of these "arguments" are often a expression of unwillingness to change old habits, and to learn something new.

Don't listen to them!

Whenever you can save a comment by refactoring a piece of code to a separate function with an expressive name, do it - it will definitely improve your code. Automatic refactoring tools make it easy, simple and safe to extract methods. And please, do not take people serious who recommend writing functions with >300 lines - such people are definitely not qualified to tell you how you should code.

share|improve this answer
16  
"Don't listen to them!": given the fact that the OP is asked by his boss to stop splitting code, the OP should probably avoid your advice. Even if the boss is unwilling to change his old habits. Also note that as highlighted by previous answers, both the code of the OP and his boss' code are badly written, and you (intentionally or not) don't mention that in your answer. – Arseni Mourzenko 9 hours ago
1  
@ArseniMourzenko: not everyone of us has to buckle before his boss. I hope the OP is old enough to know when he has to do the right thing, or when he has to do what his boss says. And yes, I was not going into the details of the example intentionally, there are enough other answers already discussing those details. – Doc Brown 9 hours ago
1  
@DocBrown Agreed. 300 lines is questionable for a whole class. A 300 line function is obscene. – JimmyJames 8 hours ago
11  
I've seen many classes that are more than 300 lines long that are perfectly good classes. Java is so verbose that you almost can't do anything meaningful in a class without that much code. So "number of lines of code in a class," all by itself, is not a meaningful metric, anymore than we would consider SLOC a meaningful metric for programmer productivity. – Robert Harvey 7 hours ago
1  
Also, I've seen Uncle Bob's sage advice misinterpreted and abused so much that I have my doubts that it is useful to anyone but experienced programmers. – Robert Harvey 7 hours ago

Don't put everything in one big loop, but don't do this too often either:

function isApplicationInProduction(headers) {
   return _.has(headers, 'resourceId');
}

The problem with the big loop is that its really hard to see its overall structure when it spans many screens. So try to take out large chunks, ideally chunks that have a single responsibility and are re-usable.

The problem with the tiny function above, is that while atomicity and modularity are generally good, that can be taken too far. If you're not going to reuse the above function it detracts from code readability and maintainability. To drill down into the detail you have to jump to the function instead of being able to read the detail inline, and the function call takes up hardly any less space than the detail itself would.

Clearly there is a balance to be found between methods that do too much and methods that do too little. I would never break out a tiny function as above unless it was going to be called from more than one place, and even then I would think twice about it, because the function just isn't that substantial in terms of introducing new logic and as such barely warrants having it's own existence.

share|improve this answer

It seems like what you actually want is this:

phoneNumber = headers.resourceId || DEV_PHONE_NUMBER

This should be self-explanatory to anyone who reads it: set phoneNumber to the resourceId if it's available, or default to DEV_PHONE_NUMBER if it's not.

If you truly want to set that variable only in production, you should have some other, more canonical app-wide utility method (that doesn't require parameters) to determine where you're running from. Reading the headers for that information doesn't make sense.

share|improve this answer

Let me be blunt: it seems to me that your environment (language/framework/class design etc.) is not really suited for "clean" code. You are mixing all possible kinds of things up in a few lines of code that should not really be close together. What business does a single function have with knowing that resourceId==undef means that you are not in production, that you are using a default phone number in non-production systems, that the resourceId is saved in some "headers" and so on? I assume headers are HTTP headers, so you even leave the decision about which environment you are in to the end user?

Factoring single pieces of that out into functions will not help you much with that underlying problem.

Some keywords to look for:

  • decoupling
  • cohesion
  • dependency injection

You can achieve what you want (in other contexts) with zero lines of code, by shifting the responsibilities of the code around, and using modern frameworks (which may or may not exist for your environment/programming language).

From your description ("300 lines of code in a 'main' function"), even the word "function" (instead of method) makes me assume that there is no point in what you are trying to achieve. In that old-school programming environment (i.e., basic imperative programming with little structure, certainly no meaningful classes, no class framework pattern like MVC or somesuch), there is really not much point in doing anything. You will never get out of the sump without fundamental changes. At least your boss seems to allow you to create functions to avoid code duplication, that is a good first step!

I know both the type of code as well as the type of programmer you are describing quite well. Frankly, if it were a co-worker, my advice would be different. But as it is your boss, it is useless for you to go fighting about this. Not only that your boss can overrule you, but your code additions will indeed lead to worse code if only you do "your thing" partially, and your boss (and probably other people) keep on like before. It may just be better for the end result if you adapt to their style of programming (only while working on this particular codebase, of course), and try, in this context, to make the best out of it.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.