Skip to main content

Clean Code - Functions

http://vunvulearadu.blogspot.ro/2014/08/clean-code-naming.html
In the last blog post we discover the universe of Clean Code written by Robert C. Martin. We had the opportunity to go deeper on the naming topic and see how easily small things like meaningful names or naming that revel intention can improve the quality and readability of the code itself.
Today we will talk dive deeper in Clean Code and we will talk about ‘Functions’. This basic and simple mechanism used to write programs can impact not only how easily a program can be maintain and extended but also the mental health of developers. Don’t forget that long methods will make your eyes bleed.
Imagine a book where all the paragraphs are mixed, the font size is different for each of it and a part of them has 20 pages. How easily you can read a book like that. Code should be written in a way that gives the opportunity to people to read it like a book, from to bottom, where each different logic is grouped separately.

Happy story

I remember one time when I had to extend an existing code written by somebody else. When I open the solution I found only one class, with 2 or 3 methods that had in total around 4.000 lines of code. My estimations for that task were:

  • 4 days on refactoring
  • 1 days to add the new functionality

My PM from that period accepted this estimation and gave me green light to work on it. But in the end, I needed 4X more time to finish the task because the methods themselves were too long and I couldn’t do anything there without having nightmares during the nights.
So, what we can do to improve the quality of developers and our software from functions/methods perspective.

Small

This is the first and the most important rule related to functions. You should keep them as short as possible. The explanation is pretty simple: a shorter function will do less (only one simple thing). Additionally to this, it will be more easily to understand and manage it.
The natural question that pops in our mind is “How short?”.

  • 100 lines?
  • 50 lines?
  • 10 lines?
  • 5 lines?

Unfortunately we cannot have a magic number like 5 or 20, because is pretty hard to generalized it. The length of a method depends on multiple factors like the code conventions. For example how often you hit enter to add a new line (for every ‘{‘ or for every logic statement and so on).
In general if you end up with a method longer than 10-15 lines of code you should take a look over it and see why is so long. Because of code conventions or it is because there is too much logic there.

Blocks and Indenting

Thing about an IF, ELSE, WHRE, REPEAT and this kind of functionality. You don’t want to end up with an IF on 10 lines. It is pretty hard to read and understand. For this kind of cases you should extract the check into a separately function and call if from the IF statement. Appling this rule you will end up with statements like IF or WHERE that needs only one line of code.
Additionally to this you will improve the readability and documentation of your own code. For developers will be very easy to understand what the code does and what the check behind that is IF or WHERE supposed to do.

Do One Thing

If you read a long function you realized that more than one thing is done there. For example in the same function you open the DB connection, you execute a query, you convert the result to another type and you handle special cases. Each of this things should be done in a separately places.
This is why a function should do ONLY one things. If you need to do more than one thing, than you should split it into separately functions.
Even if the statement is so simple, it is pretty hard to do this. If you discover in a function different part of code that is grouped or you can extract a part of it into a separate function with a meaningful name than the functions is doing more than one thing.
One level of abstraction per Function
This can be a mechanism that can tell us that the function is doing to many things. For example a function that process an entity and also internally start to split and transfer one of the entity fields has more than one level of abstraction.
It is pretty clear that you should extract the string processing into another function. In this way each function will have only one level of abstraction.

Switch Statement

The store related to switch statements is pretty long and we will talk about with another occasions also. In our case we should extract the logic of each of CASE’s to separate functions. Even if we will do this thing will not be okay because we are violating SRP (Single Responsibility Principle).
We should replace the switch statement with polymorphism. This is the perfect solution, but there are cases when such a solution would add to much extra complexity. When you need to use switch statement you should hide them as deep as possible, in Clean Code the recommendation is behind a abstract factory.

 Use descriptive names

The name of a function should describe exactly what it does. Not less or more. For example a function with name like ‘DO’, ‘ACTION’ are not very helpful, because we don’t know what are there scope.
 A name like ‘TriggerDoorLock’ it gives us all the information that is needed to know what that function is doing.
Finding a good name is pretty hard and can consume a lot of energy. Addition to good names you should be consistent and try to use the same naming pattern when there are similarities.

Function Arguments

How many arguments a function should have? The best value is 0, but not all the time is possible. Each time when you add a new argument think about the role of it.
When you end up with more than 3 or 4 arguments something can be wrong. You should take a look over them and see if you cannot more the function in another location or to add another abstraction level.
The OUT option to arguments is not all the time recommended and it may be a smell that something is wrong there. For example the TryXXX methods usually check if a conversion can be done. If it can be done with success, ‘TRUE’ is retuned and the out parameter will contain the conversion result. This could be a smell that the method is doing   too many things – converting and checking.

Have No Side Effects

This is the case when your function is doing more than one thing, but without telling to the client. For example a ‘Read’ method that read the content of a file and after it delete it without notify the user. In this case the user should be notified about this action or at least he should know about it from the moment when he does the call – ‘ReadAndDelete’.
Because of this side effects we can end up with temporary coupling. For example when function ‘GoLeft’ can be called only if ‘StartEngine’ was called. You should think about a way to expose only the methods that are available at a specific time, without creating temporarily coupling. For example the ‘StartEngine’ could return an object that has only the commands like ‘GoLeft’, ‘GoRight’ and so on.

Command Query Separation

A function should do only one thing. You should never have methods that execute a query and in the same time a command. This two action needs to be separately all the time without exception.

Prefer Exceptions and not Error Codes

Returning an error code created two additional things that needs to be done by developer/client. He needs to know the mapping of each error code and in the same time he has to check the returning error code.
For this cases throwing an exception is better and will simplify the work of clients. Additional to this, the error handing will be 100% separately from your logic.

Extract try/catch blocks 

A code that contains this kind of blocks are pretty ugly and long. Because of this all this blocks of code should be extracted into separately functions. The TRY block can be putted into a function and the catch block can be putted into another functions.

Don’t repeat yourself

All the code that is duplicated should be extracted to a separately function. You will not only reduce the number of lines of code, but you would also male your life easier when a change will need to be done. It is easier to change only one line of code than searching and changing all the locations where the code is duplicated.

Small things

As you can see, small things can make a real difference between a good and a bad function. You don’t need to do or know crazy stuff to be able to write ‘happy’ functions. Taking into consideration this recommendation you will end up with better software that can be maintain after 10 years easier and with less money.

Comments

Popular posts from this blog

Windows Docker Containers can make WIN32 API calls, use COM and ASP.NET WebForms

After the last post , I received two interesting questions related to Docker and Windows. People were interested if we do Win32 API calls from a Docker container and if there is support for COM. WIN32 Support To test calls to WIN32 API, let’s try to populate SYSTEM_INFO class. [StructLayout(LayoutKind.Sequential)] public struct SYSTEM_INFO { public uint dwOemId; public uint dwPageSize; public uint lpMinimumApplicationAddress; public uint lpMaximumApplicationAddress; public uint dwActiveProcessorMask; public uint dwNumberOfProcessors; public uint dwProcessorType; public uint dwAllocationGranularity; public uint dwProcessorLevel; public uint dwProcessorRevision; } ... [DllImport("kernel32")] static extern void GetSystemInfo(ref SYSTEM_INFO pSI); ... SYSTEM_INFO pSI = new SYSTEM_INFO(

Azure AD and AWS Cognito side-by-side

In the last few weeks, I was involved in multiple opportunities on Microsoft Azure and Amazon, where we had to analyse AWS Cognito, Azure AD and other solutions that are available on the market. I decided to consolidate in one post all features and differences that I identified for both of them that we should need to take into account. Take into account that Azure AD is an identity and access management services well integrated with Microsoft stack. In comparison, AWS Cognito is just a user sign-up, sign-in and access control and nothing more. The focus is not on the main features, is more on small things that can make a difference when you want to decide where we want to store and manage our users.  This information might be useful in the future when we need to decide where we want to keep and manage our users.  Feature Azure AD (B2C, B2C) AWS Cognito Access token lifetime Default 1h – the value is configurable 1h – cannot be modified

What to do when you hit the throughput limits of Azure Storage (Blobs)

In this post we will talk about how we can detect when we hit a throughput limit of Azure Storage and what we can do in that moment. Context If we take a look on Scalability Targets of Azure Storage ( https://azure.microsoft.com/en-us/documentation/articles/storage-scalability-targets/ ) we will observe that the limits are prety high. But, based on our business logic we can end up at this limits. If you create a system that is hitted by a high number of device, you can hit easily the total number of requests rate that can be done on a Storage Account. This limits on Azure is 20.000 IOPS (entities or messages per second) where (and this is very important) the size of the request is 1KB. Normally, if you make a load tests where 20.000 clients will hit different blobs storages from the same Azure Storage Account, this limits can be reached. How we can detect this problem? From client, we can detect that this limits was reached based on the HTTP error code that is returned by HTTP