How to measure code quality? Top code quality metrics

August 1, 2024

How to measure code quality? Top code quality metrics

Ready to start?

What is code quality?

Engineering organizations have to balance the speed and quality of the services, features and updates that they deliver to customers. The foundation of the quality is the code; the code has to meet expectations of the customer - any defects, errors or issues with user experience can have a damaging effect on the reputation of the engineering organization. 

The quality of code is integral to everything you do in your software development lifecycle. It impacts how easy it is to make changes, the functionality and readability of code, the user experience, the product’s success and the long-term management of the code including costs for labor and maintenance, as well as time to market. 

Good quality code provides a better user experience; it’s easy to understand and update, it promotes reuse (and collaboration), improves software performance, reduces bugs and errors, provides a good foundation to build upon new capabilities and features, and will ultimately provide confidence to your customers. 

Bad quality code can be a drain on resources; taking more time for developers to fix and work on due to complexity, and limiting the ability to reuse or build upon the code. It can be error prone, expensive to maintain, and has a domino effect in providing a bad user experience for developers, system architects, DevOps, SREs and customers.  

Ultimately, the quality of code has an impact on the success of an organization.

The importance of code quality metrics

Code quality is an important priority, and so measuring and monitoring the quality of code is essential. As development teams have to deal with changes to existing on a frequent basis, they have to ensure that any additions, deletions, updates or reuse of code adhere to high quality standards and watch out for any degradation. The best way to do this is to track code quality metrics, as they empower teams with data that shows that the code is readable, maintainable, reliable and performing as it should. Code quality metrics provide an unbiased assessment of a codebase using real, measurable indicators of software quality.

Metrics enable teams to analyze the code’s health so that the development team knows where they need to make improvements. They can also provide benchmarks so that their performance can be measured against the high standards. This also sets a standard-bearer so that everyone in the engineering team knows exactly what code quality looks like - helping to improve the engineering excellence within the organization. 

8 Key code quality metrics

Code quality metrics help to inform you on whether the value of the codebase is good or bad. This assessment is made in relation to the standards and expectations set by the engineering team. Here are the top code quality metrics to track: 

1. Cyclomatic complexity

Cyclomatic complexity quantifies the complexity of a program by counting its decision points, thereby determining the number of linearly independent paths through the code. Lower cyclomatic complexity indicates simpler, more manageable code, which reduces the likelihood of errors and enhances maintainability. Essentially, it helps assess the code’s readability and the risk associated with changes. This is the key measure for code complexity, but there are others such as Halstead complexity, which evaluates program vocabulary, length, and cognitive load.

2. Average code review time

Average code review time is exactly what it sounds like - longer review times can impact the velocity of the team, morale about the process and can be detrimental for code health. Reviews that are too fast might lack thoroughness. By monitoring and benchmarking this metric, teams can make changes to their review process. This particular metric is indirectly about the code quality itself - and can actually be used as a developer productivity metric. 

3. Code churn 

Code churn measures how much code is changing within a specific time frame, for example if code is rewritten or deleted just after being written. It can identify patterns such as excessive changes that indicate volatility and uncertainty. If not dealt with, this can hinder and slow down the development process. By monitoring code churn, engineering teams can track the efficiency of the team’s workflow and the quality of contributions of developers.

4. Code coverage

Code coverage, otherwise known as test coverage, is typically quantified as the proportion of your code that has been tested automatically. It serves as a crucial marker of your software's robustness and reliability; a higher coverage suggests a greater likelihood of maintaining bug-free code. Code coverage motivates developers to write comprehensive tests and to use better design practices so that the code will pass these tests. Areas that have lower test coverage may be riskier to change.

5. Code duplication 

Code duplication is when the same code is used in more than one location within a software system. It can lead to several issues including maintenance overhead as each duplicate has to be updated individually when changes are required, and can also lead to inconsistencies and increased bug surface.

6. Code maintainability index 

The code maintainability index uses a scale of 0-100 to quantify how easy it is to maintain the code. It uses a number of other code metrics such as number of lines of code, the documentation, standardization in the coding, cyclomatic complexity and Halstead complexity to provide a score. Engineering teams can benefit from this metric as it can provide a good overview of the state of the codebase - including whether it has many defects and technical debt - which makes it easier for developers that are onboarding, as well as for existing developers that are joining a new project. The index can also reduce the costs of maintenance of the software.

7. Technical debt

Just as financial debt involves having to pay back interest over time, which can compound existing difficulties, technical debt can have a knock-on effect and cause damage in a number of ways if it isn’t resolved promptly. The metric is to compare and contrast the cost of choosing a quick fix for the short-term, rather than using a better approach that would have more of a long-term impact. The higher the technical debt, the more work that is required to improve the codebase - which in turn means less time that the team is able to add new features and functionality, which ultimately stifles innovation. 

8. Dependency graph complexity 

This maps the connections and relationships between components, modules or functions within software. The higher number of dependencies and connections can mean more complexity, which can make changes to code harder to facilitate. 

Improve code quality with an internal developer portal 

An internal developer portal can bring together all of the metrics from your code quality tools into one centralized place. The main benefit of the portal is being able to provide additional context to those metrics through a single source of truth. With a portal, you can:

  • Unify code quality metrics with other quality metrics
    Utilize scorecards to integrate code quality metrics with other quality-related metrics, providing you with a comprehensive quality scorecard that ensures standards are maintained across all aspects.
{{cta_1}}

Check out Port's pre-populated demo and see what it's all about.

Check live demo

No email required

{{cta_2}}

Contact sales for a technical product walkthrough

Let’s start
{{cta_3}}

Open a free Port account. No credit card required

Let’s start
{{cta_4}}

Watch Port live coding videos - setting up an internal developer portal & platform

Let’s start
{{cta_5}}

Check out Port's pre-populated demo and see what it's all about.

(no email required)

Let’s start
{{cta_6}}

Contact sales for a technical product walkthrough

Let’s start
{{cta_7}}

Open a free Port account. No credit card required

Let’s start
{{cta_8}}

Watch Port live coding videos - setting up an internal developer portal & platform

Let’s start
{{cta-demo}}
{{reading-box-backstage-vs-port}}

Example JSON block

{
  "foo": "bar"
}

Order Domain

{
  "properties": {},
  "relations": {},
  "title": "Orders",
  "identifier": "Orders"
}

Cart System

{
  "properties": {},
  "relations": {
    "domain": "Orders"
  },
  "identifier": "Cart",
  "title": "Cart"
}

Products System

{
  "properties": {},
  "relations": {
    "domain": "Orders"
  },
  "identifier": "Products",
  "title": "Products"
}

Cart Resource

{
  "properties": {
    "type": "postgress"
  },
  "relations": {},
  "icon": "GPU",
  "title": "Cart SQL database",
  "identifier": "cart-sql-sb"
}

Cart API

{
 "identifier": "CartAPI",
 "title": "Cart API",
 "blueprint": "API",
 "properties": {
   "type": "Open API"
 },
 "relations": {
   "provider": "CartService"
 },
 "icon": "Link"
}

Core Kafka Library

{
  "properties": {
    "type": "library"
  },
  "relations": {
    "system": "Cart"
  },
  "title": "Core Kafka Library",
  "identifier": "CoreKafkaLibrary"
}

Core Payment Library

{
  "properties": {
    "type": "library"
  },
  "relations": {
    "system": "Cart"
  },
  "title": "Core Payment Library",
  "identifier": "CorePaymentLibrary"
}

Cart Service JSON

{
 "identifier": "CartService",
 "title": "Cart Service",
 "blueprint": "Component",
 "properties": {
   "type": "service"
 },
 "relations": {
   "system": "Cart",
   "resources": [
     "cart-sql-sb"
   ],
   "consumesApi": [],
   "components": [
     "CorePaymentLibrary",
     "CoreKafkaLibrary"
   ]
 },
 "icon": "Cloud"
}

Products Service JSON

{
  "identifier": "ProductsService",
  "title": "Products Service",
  "blueprint": "Component",
  "properties": {
    "type": "service"
  },
  "relations": {
    "system": "Products",
    "consumesApi": [
      "CartAPI"
    ],
    "components": []
  }
}

Component Blueprint

{
 "identifier": "Component",
 "title": "Component",
 "icon": "Cloud",
 "schema": {
   "properties": {
     "type": {
       "enum": [
         "service",
         "library"
       ],
       "icon": "Docs",
       "type": "string",
       "enumColors": {
         "service": "blue",
         "library": "green"
       }
     }
   },
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {
   "system": {
     "target": "System",
     "required": false,
     "many": false
   },
   "resources": {
     "target": "Resource",
     "required": false,
     "many": true
   },
   "consumesApi": {
     "target": "API",
     "required": false,
     "many": true
   },
   "components": {
     "target": "Component",
     "required": false,
     "many": true
   },
   "providesApi": {
     "target": "API",
     "required": false,
     "many": false
   }
 }
}

Resource Blueprint

{
 “identifier”: “Resource”,
 “title”: “Resource”,
 “icon”: “DevopsTool”,
 “schema”: {
   “properties”: {
     “type”: {
       “enum”: [
         “postgress”,
         “kafka-topic”,
         “rabbit-queue”,
         “s3-bucket”
       ],
       “icon”: “Docs”,
       “type”: “string”
     }
   },
   “required”: []
 },
 “mirrorProperties”: {},
 “formulaProperties”: {},
 “calculationProperties”: {},
 “relations”: {}
}

API Blueprint

{
 "identifier": "API",
 "title": "API",
 "icon": "Link",
 "schema": {
   "properties": {
     "type": {
       "type": "string",
       "enum": [
         "Open API",
         "grpc"
       ]
     }
   },
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {
   "provider": {
     "target": "Component",
     "required": true,
     "many": false
   }
 }
}

Domain Blueprint

{
 "identifier": "Domain",
 "title": "Domain",
 "icon": "Server",
 "schema": {
   "properties": {},
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {}
}

System Blueprint

{
 "identifier": "System",
 "title": "System",
 "icon": "DevopsTool",
 "schema": {
   "properties": {},
   "required": []
 },
 "mirrorProperties": {},
 "formulaProperties": {},
 "calculationProperties": {},
 "relations": {
   "domain": {
     "target": "Domain",
     "required": true,
     "many": false
   }
 }
}
{{tabel-1}}

Microservices SDLC

  • Scaffold a new microservice

  • Deploy (canary or blue-green)

  • Feature flagging

  • Revert

  • Lock deployments

  • Add Secret

  • Force merge pull request (skip tests on crises)

  • Add environment variable to service

  • Add IaC to the service

  • Upgrade package version

Development environments

  • Spin up a developer environment for 5 days

  • ETL mock data to environment

  • Invite developer to the environment

  • Extend TTL by 3 days

Cloud resources

  • Provision a cloud resource

  • Modify a cloud resource

  • Get permissions to access cloud resource

SRE actions

  • Update pod count

  • Update auto-scaling group

  • Execute incident response runbook automation

Data Engineering

  • Add / Remove / Update Column to table

  • Run Airflow DAG

  • Duplicate table

Backoffice

  • Change customer configuration

  • Update customer software version

  • Upgrade - Downgrade plan tier

  • Create - Delete customer

Machine learning actions

  • Train model

  • Pre-process dataset

  • Deploy

  • A/B testing traffic route

  • Revert

  • Spin up remote Jupyter notebook

{{tabel-2}}

Engineering tools

  • Observability

  • Tasks management

  • CI/CD

  • On-Call management

  • Troubleshooting tools

  • DevSecOps

  • Runbooks

Infrastructure

  • Cloud Resources

  • K8S

  • Containers & Serverless

  • IaC

  • Databases

  • Environments

  • Regions

Software and more

  • Microservices

  • Docker Images

  • Docs

  • APIs

  • 3rd parties

  • Runbooks

  • Cron jobs

Starting with Port is simple, fast and free.

Let’s start