/Angular

The subtle art of writing clean code

When I came across the term clean code, I thought its something that only 10x Engineers or Senior developers can do. We often tend to approach a solution that works and let it be like that, just because it works. We always say we can go back at a later time and refactor things. But it will never happen. You will leave it there to move on to the next feature to avoid delays in your production cycle.

” It is not the language that makes programs appear simple. It is the programmer that make the language appear simple! ” – Robert C.Martin

Below is the list of some of the common pitfalls or code smells which even I did during my development stage.

Avoid Too Many conditions

Let’s imagine a common example in this case. In any application, there might be different types of licenses that are provided and you will have to validate them upon user request. The initial code which validated the license was like this.

if(license.edition.Equals("Gold") || license.edition.Equals("Platinum") || license.edition.Equals("CouponCode"))
{
  //Do Something
}

This looks fine, but when you read the code you will have to scroll the sidebar to read the entire set of conditions. To refactor this we can make use of c# Extension methods and make the code more clean and readable.

internal static class Extensions
{
   internal static bool ValidateLicenseEditions(this License license)
   {
	return (license.LicenseInfo.LicenseEdition.Equals("Gold") ||
		license.LicenseInfo.LicenseEdition.Equals("Platinum") ||
		license.LicenseInfo.EnvironmentType.Equals("CouponCode"))
   }
}

So as a result of this, the old validation code can be changed like this which looks much better and more readable.

if(license.ValidateLicenseEditions())
{
  //Do Something
}

You can indeed ask why an extension method, instead i can write an ordinary method and pass in the License object and do the same,

but any additional method you write should be added to the interfaces. To avoid the chaos of writing 20 methods under the ILicense interface simply write an extension method.

Don’t write comments. We write self-documenting code

Very large classes

If people take too may responsibilities they suffer. Similar to that classes also suffer if they have multiple responsibilities. To rectify this follow the SOLID principle, and split your class with multiple interfaces. Replace, remove redundant and duplicated code.

First do it, then do it right, then do it better.

Follow DRY principle

This is one of the most common ways that focuses on the DRY principle. If you are not aware of it,

Don’t Repeat Yourself

The DRY principle is aimed at reducing the repetition of software patterns, replacing them with abstractions; and several copies of the same data, using data normalization to avoid redundancy. Every duplicate line into application needs to be maintained. If a potential source of bugs appears, it would have to be fixed in all of those duplicates. It bloats the codebase making it much more difficult for developers to fully understand the entire system.

Don’t reinvent the wheel

If there’s already a library for serializing and de-serializing objects, you don’t have to reinvent the same. It has already been used by millions of people worldwide and it might have 1000+ revisions and bug fixes, so if you are reinventing the wheel from the first, its just not worth the time and development effort. That said you should also not install libraries just for the sake of solving one single problem.

A real-world example would be, in every application you need to validate some requests from the user. When a user signs up you will have to validate the form details and return a proper error message if any. A simple validation method would look like this

if (!string.IsNullOrWhiteSpace(form.Name)){
    return new BadRequestObjectResult(new { Error = "Name is required." });
}

if (!string.IsNullOrWhiteSpace(form.Email)){
    return new BadRequestObjectResult(new { Error = "Email is required." });
}

if (!Regex.IsMatch(form.Email, @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$")){
    return new BadRequestObjectResult(new { Error = "Email is not a valid email address." });
}

if (!string.IsNullOrWhiteSpace(form.Message)){
    return new BadRequestObjectResult(new { Error = "Message is required." });
}

if (form.Message.Length > 100){
    return new BadRequestObjectResult(new { Error = "Message cannot have more than 100 characters." });
}

So now we have an ugly pile of if blocks near the top of our function to help determine the validity of the inputs. If one of the properties is found to be invalid, we immediately return a BadRequestObjectResult with a description of the validation infraction. This ends any further processing of our function and sends an HTTP 400 response back to the client.

Fluent Validation

To simplify the pile of if blocks and make the code more readable, we can make use of this library called FluentValidation . Using this you can quickly build validation rules for your request objects without altering your core business logic. The below example shows the validator example.

public class SignUpValidator : AbstractValidator<SignUp>
{
    public ContactFormValidator()
    {
        RuleFor(x => x.Name).NotEmpty();
        RuleFor(x => x.Email).NotEmpty().EmailAddress();
        RuleFor(x => x.Message).NotEmpty().MaximumLength(100);
     }
}

Then in your service class, using just two lines of code you can validate the request.

var validator = new SignUpValidator();
var validationResult = validator.Validate(form);
if(validationResult.IsValid){
  //proceed to signup
}

Conclusion

Notice how much the library helped us solve validation problems, so this can be re-used wherever applicable. So make use of nice libraries to write clean, simple and maintainable code.

“So if you want to go fast, if you want to get done quickly, if you want your code to be easy to write, make it easy to read.” ― Robert C. Martin
HariHaran

HariHaran

Full Stack Developer

Read More