Top Best Practices to Build High Quality Software

Adhithi Ravichandran
6 min readJan 30, 2023
Photo by Kelly Sikkema on Unsplash

Technologies and frameworks change, but architecture principles, and best practices in writing high quality code have remained the same!

Here is a LinkedIn post that I posted a few days ago, and decided to write a follow up article on what I think are the best practices to build high quality software today!

Here are the best practices that I would focus on while building high quality software:

1. Don’t Over-Engineer

Lot of the times, architects tend to solve problems that don’t exist today. This is not necessary and you will end up with a cobweb architecture and code that was designed for the future use-case that may never happen!

Over engineering results in unnecessary abstractions and results in complex code. Although you want your code to be scalable in the future, designing for the future and abstracting away for future use-cases is not the ideal way to architect your software. Shipping high quality software within a reasonable time frame is of utmost priority, and architects need to solve the problems that the market/end user is facing today. Stick to the requirements of today, without trying to solve complex problems of tomorrow.

Solve only the problems that you have today!

2. Modular Code

It is important to keep in mind the separation of concerns while architecting your software. Break down the system based on different functional areas into independent, interchangeable modules. This results in modular code with well defined areas.

Some of the common techniques in writing modular code are:

Don’t Repeat Yourself (DRY)

This is a famous technique, that lets you consolidate repeatable code into reusable units.

Functions should do one thing

Your function or components should always do only one thing. If it can do more than one thing, it is time to break it apart. Refactor functions so that they are reusable small units, achieving one thing.

3. Readable Code

Writing readable code is super important, and there is no pride in writing complex code that no one else can understand! Introducing unnecessary complexity adds no value to the code.

“The ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. …[Therefore,] making it easy to read makes it easier to write.”

Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship

Always keep in mind that few months or years down the lane, someone is going to read your code. It could be a newbie developer or an experienced architect. They will be reading your code to try fix a bug, help solve a problem in the system, or use existing patterns to extend the system. Readable code will save them hours of time and wouldn’t it be nice to know that someone in the future is thanking you for your readable code?!

4. Maintainable Code

The simpler and more modular your code is, the easier it is to maintain. Think of how your future team mates will maintain this codebase.

5. Automated Tests

Ensure your code is tested with, tons of unit tests, lots of integration tests and some End to End tests. Testing shouldn’t be an aftermath, and should be part of your development process.

Here is the famous testing pyramid for automated tests:

Testing pyramid — Automated tests

The bottom of the pyramid here is the Unit tests. Ideally you would write tons of unit tests for your code. This will test each individual unit of your code, like a function or a component. It is usually inexpensive, and runs the fastest.

The middle layer of the pyramid is the integration tests, which will typically be tests you write between your frontend and backend. These are a bit more expensive and slower, so you can write enough tests to cover the base use-cases and ensure that the systems are integrated.

On the top of the pyramid is End-to-End tests, and these test the entire application end to end. These are usually more expensive and slower, and you will write fewer of them.

The idea is to write lots of automated tests at the granular level, and fewer of them at the higher level. Automated tests help build the confidence you have in your code, and improves code quality by heaps and bounds.

6. Create Coding Standards

Create coding standards for the team and automate validation of the standards. New team mates can simply utilize the linters to ensure they are following the team’s coding standards. Everyone coding within the team should follow the same standards, resulting in consistency.

7. Code Reviews

This is crucial to all teams. Even with all the automations in place, a manual code review is absolutely necessary.

This is where you will review for code accuracy, design, tests and so on. Ensure that each Pull Request (PR) out for code review solves one problem, and is small and targeted.

When creating Pull Requests for my code review, here is a checklist that I like to follow:

✅ Description with bullet points, that explains what the code is accomplishing

✅ Appropriate links to tickets

✅ Keep it small and simple

✅ Attach screenshots ( if applicable )

✅ Respond to feedback and questions promptly

✅ Ensure you have run the linter, tests, test coverage and other cosmetic stuff so the reviewer can focus on the important aspects of your code.

Ensure you ship high quality code, by keeping it simple, clear, and concise!

8. Automate CI/CD

Automating the CI/CD process ensures that your team is checking in code continuously that builds, and passes the minimum code quality checks. Include checks for detection of code smells, test coverage, linting, etc..

9. Security Review

Hackers are everywhere, make sure you account for potential vulnerabilities. Include security audits as a part of the team’s process to protect against vulnerabilities.

10. Well Documented

Your code may look beautiful to you and you may think of it as a piece of art! But you still have to document it!

A solid documentation is necessary to clearly describe your architecture, components, system constraints, dependencies and so on. This is useful in creating a shared understanding to all stakeholders, and also crucial while onboarding new team members.

11. Think about the End User

Your end-user is ultimately going to to use this product, ensure you have their best interests in mind. Ensure that accessibility is given importance, and the code follows the latest accessibility guidelines and standards. Engage in user reviews and look for feedback before releasing the product to a broad range of users.

✅ Rinse and Repeat

Rinse and repeat all of these in your future projects irrespective of the tech stack!

Hi! I am Adhithi Ravichandran, a Software Consultant, Author and Speaker based in Kansas City. I am the owner and founder of Surya Consulting, Inc. through which I provide various software consulting, architecture, and teaching services.

I am passionate about technology and teaching. Through my Pluralsight courses, I have taught over 80,000 students topics such as React, React Native, GraphQL, Next.js, and Cypress.

For more info visit:

Pluralsight Courses

Connect with on Twitter: @AdhithiRavi



Adhithi Ravichandran

Software Consultant, Author, Speaker, React|Next.js|React Native |GraphQL|Cypress Dev & Indian Classical Musician