Demystifying code quality, one technique at a time

I shall start with two laws written 40+ years ago about software evolution by Lehman & Bélády:

  1. Law of continuous change: Any software system used in the real-world must change or become less and less useful in that environment.
  2. Law of increasing complexity: As a system evolves, its complexity increases unless work is done to maintain or reduce it.

Being a software engineer, I often emphasize a lot about code quality along with other aspects of application development. A great deal of effort is required for writing clean code. In the early days, I was blissfully ignorant of the damage I was planting for my co-programmers through my code. As a novice in the world of paid(entry level) developer position at LiteBreeze, I was fortunate enough to land a tutelage under master developers. These experts passed their wisdom through scrutinized code reviews. Due to the current packed schedule I am unable to pass this information to others. Hence, I decided to document a few sections. The list only covers selected fundamentals in an easy digestible format.

Training

There’s a famous poem by Zen

To follow the path:
look to the master,
follow the master,
walk with the master,
see through the master,
become the master.

Our Project director has always quoted the following sentence:
Three things in life which require practice 

  1. Driving
  2. Swimming
  3. Programming 

Hence to be a good programmer, you require the right mindset, practice & the best apprenticeship. To sum up: Programming is a collection of acquired SKILLS.

Naming

Always use intention revealing names – the name of a variable, function, or class, should answer all the big questions. It should tell you why it exists, what it does, and how it is used[1].

Functions

A function should only do one thing and that one thing well. Software entities should be open for extension but closed for modifications (aka Open-closed principle[2]).

Dead code and variables

LiteBreeze employs an agile model for software development. Requirements keep changing and so does the code. Remove unused code/variables which doesn’t affect the application. Apart from shrinking the program size, it avoids the execution of unnecessary functionality. We can always gracefully recover from this deletion using version control software.

Hard coding

Avoid hard coding values, use environment variables instead. This is important as values differ in development, staging and production. Laravel users can take one step further by utilizing configuration caching

Long IF conditions

A complex/lengthy IF condition will be difficult for a programmer to comprehend the logic involved in it. Moreover, you are demanding the developer to hold/process all the half-finished thoughts in their head. Simplify long IF conditions with smaller functions which promote easy perception.

Error handling

Always practice the use of TRY-CATCH-FINALLY blocks for functions which generate unexpected outputs. In this manner, we increase the reliability/robustness of the application. 

Foreign keys

Most of our applications use a relational database for storage. Always declare a foreign key column explicitly. Indexes on foreign key columns can provide performance benefits for table joins involving the primary and foreign key. If you are tasked with maintenance of an existing project which doesn’t have foreign keys, make sure to index the foreign key columns.

Avoid SQL inside loops

Avoid writing SQL queries inside loops as it exploits the system resources.

Convention over configuration

If we are using a framework for development, always strive to follow the framework’s convention rather than a custom developer configuration. We use a framework to increase our productivity. By following convention, introducing a new developer versed in the framework can identify/relate the working with ease. Additionally, a future update of the framework won’t hamper the application.

HTTP error codes

Understand and use HTTP error codes. Use 404 when a page isn’t found. It should not be used when a requested resource(eg: user, post) is not found in the datastore. Rather, when a requested resource is missing, the browser should be redirected to the resource listing page with an appropriate message.

Technical debt


The image in the tweet paints a very good picture of technical debt. Clean/good code means sustained development. The quality of code today is directly related to the ease of development/extension tomorrow. Each step we take today to write good code provides compounding benefits to the application. Besides, it benefits oneself as a developer by improving our skill set. If the quality of the code isn’t good, future changes become perilous and complicated. 

Duplicate code

Never duplicate code. If duplicated code requires modification, there’s a danger that one section of the code is updated without checking further for other instances. Duplicate code can be fixed by refactoring it into its own function. 

Separating code and presentation

Avoid writing HTML inside Javascript. HTML can be passed to Javascript either by using a variable or by using AJAX calls. This processing method will provide more clarity in separating code and presentation logic. Separation of concerns minimizes time dependency and allows for concurrent development. Moreover, if a change in design is to be carried out, the designer needs to only refer to the HTML templates rather than the Javascript code. Also, this separation promotes readability. It is easier to read business(code) logic that is not intermingled with presentation logic & vice-versa.

YAGNI

Is an abbreviation for You aren’t gonna need it. A programmer should not add functionality until deemed necessary. XP co-founder Ron Jeffries explains it as: Always implement things when you actually need them, never when you just foresee that you need them. Even if you’re totally, totally, totally sure that you’ll need a feature, later on, don’t implement it now. Usually, it’ll turn out either a) you don’t need it after all, or b) what you actually need is quite different from what you foresaw needing earlier[3].

KISS

Is an acronym for Keep it simple, stupid. The KISS principle states that most systems work best if they are kept simple rather than made complicated; therefore, simplicity should be a key goal in design, and unnecessary complexity should be avoided[2].

Premature optimisation is the root of all evil

Premature optimisation happens when a programmer lets performance considerations affect the design of a piece of code[2]. Developers waste enormous amounts of time thinking about the speed/memory usage of their programs. In other words, developers often tend to optimise the code upfront before completing the functionality. Instead, the developers should initially code the program to complete the task. Then make sure to verify the code can be maintained. And finally, address performance[3].

Commits and Formatting

Code is read more often than it is written. Commits shouldn’t contain any debugging codes. Follow coding standards & indentation style relevant to the framework. Hence formatting viz. indentation, line breaks, splitting lengthy conditions all add value to your code. 

Communication

Communication skills are very valuable for programmers; especially due to the global nature of software projects these days. With English being the universal language, learning functional English and being able to convey your message without spelling mistakes, grammatical errors etc. is a must.

Bonus: tips for developing features

Our Swedish manager always asks us to keep a To-do list. This is done to keep tabs of pending/upcoming tasks. Similarly, when reading Kent Beck’s book about programming, he would jot down the feature requirements as To-do tasks. As a task gets coded, he would mark it as completed. If he finds a new problem which arose while coding the task, he would add it to the To-do tasks. Picking each task one at a time lets you complete the feature in an organised manner. I would suggest the Plain-tasks package for Sublime Text enthusiasts.

Laravel application guidelines

  1. Strictly follow PSR-12. Use camelCase for naming variables/methods etc.
  2. When dealing with session and file uploads – use Laravel’s Session/Storage helpers respectively. This way we can swap the session-driver/file-system depending on the environment.
  3. When using Query builder, make sure that deleted_at field is added to the query to exclude soft-deleted records.
  4. Follow the URI/Route naming convention as depicted in the image below. This means that we should use the following convention /resource/{photo}/action.
  5. Do not commit the .env file. Any change in .env file should be reflected in the .env.example & committed to version control.
  6. All DB changes should use Laravel migrations. Unless handling a specific scenario, utf8mb4_unicode_ci can be used as a de facto collation for DB, tables & columns.
  7. When any change to the composer.json file is made, always commit the latest composer.lock file too.
  8. Use composer install to install versioned dependencies which stick to the composer.lock file specifications.
  9. Refrain from editing composer.json file manually, rather use the command composer require new/package
  10. Avoid writing inline JS/CSS. None of our JS code should pollute the global namespace, specifics for avoiding this have been portrayed in a sample file.

Basic developer testing checklist

  1. Make sure pagination considers all form filters. 
  2. Radio buttons/Checkbox should allow click selection on their label. 
  3. Empty rows should cover all columns.
  4. Check for entity uniqueness(email, mobile) in form submissions.
  5. When processing inputs using AJAX, test by using single/double quotes, forward slash/backslash, backtick, Euro, Dollar symbols, Scandinavian/Latin character(Å,å,Ä,ä,Ö,ö Å,å,Æ,æ,Ø,ø) etc.
  6. When authentication with external plugins like Twitter, GitHub – make sure to use their unique-id for identifications.
  7. Always log IP address before payments. Make sure the payment page is generated from a POST request to disable page refresh & secure the transaction.
  8. Test the working of a feature with a different user which is not used for development. 
  9. When the file upload is handled, check for file size and its extension. 
  10. When using the Laravel Auth package, disable Register functionality if it isn’t required. 
  11. Make sure that list contents are alphabetically sorted.

Flawless code isn’t always feasible. Nevertheless, we should strive to upskill. All it takes is a little diligence, creativity and adherence to best practices. 

References:

  1. Clean Code
  2. Wikipedia
  3. Cunningham & Cunningham

Leave a Reply

Your email address will not be published. Required fields are marked *