Improvement of coding practices to reduce cyclomatic complexity
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/complex", func(c *gin.Context) {
param1 := c.Query("param1")
param2 := c.Query("param2")
param3 := c.Query("param3")
param4 := c.Query("param4")
param5 := c.Query("param5")
if param1 == "value1" {
c.JSON(200, gin.H{
"message": "value1",
})
} else if param2 == "value2" {
c.JSON(200, gin.H{
"message": "value2",
})
} else if param3 == "value3" {
c.JSON(200, gin.H{
"message": "value3",
})
} else if param4 == "value4" {
c.JSON(200, gin.H{
"message": "value4",
})
} else if param5 == "value5" {
c.JSON(200, gin.H{
"message": "value5",
})
} else {
c.JSON(200, gin.H{
"message": "default",
})
}
})
r.Run()
}
This is a simple example of a Go backend application using the Gin framework. It has a single endpoint
/complex
that accepts five different query parameters:
param1
,
param2
,
param3
,
param4
,
param5
. Depending on the value of these parameters, a different response is returned.
The cyclomatic complexity of this code is high due to the multiple conditional branches (if-else statements). Cyclomatic complexity is a software metric used to indicate the complexity of a program. It is a quantitative measure of the number of linearly independent paths through a program's source code.
High cyclomatic complexity increases the risk of bugs and makes the code harder to maintain and understand. It also increases the difficulty of testing since more test cases are required to achieve good code coverage. In this case, each new parameter added increases the complexity and the number of test cases needed.
Moreover, the use of multiple if-else statements can lead to nested code and increase the likelihood of errors. This can further lead to potential security vulnerabilities, making it difficult for developers to identify and resolve them.
In this case, each new query parameter increases the cyclomatic complexity, making the code more difficult to understand and maintain. This could potentially lead to the introduction of new security vulnerabilities and make their detection and resolution more difficult.
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/complex", handleRequest)
r.Run()
}
func handleRequest(c *gin.Context) {
params := map[string]string{
"value1": "value1",
"value2": "value2",
"value3": "value3",
"value4": "value4",
"value5": "value5",
}
for key, value := range params {
if c.Query(key) == value {
c.JSON(200, gin.H{
"message": value,
})
return
}
}
c.JSON(200, gin.H{
"message": "default",
})
}
The original code had a high cyclomatic complexity due to the use of multiple if-else statements. This made the code difficult to understand, maintain, and potentially introduced new security vulnerabilities.
The refactored code reduces the cyclomatic complexity by using a map to store the parameter-value pairs and their corresponding messages. This approach simplifies the code and makes it easier to understand and maintain.
In the
handleRequest
function, we iterate over the map and check if the query parameter matches any of the keys in the map. If a match is found, we return a JSON response with the corresponding message. If no match is found after iterating over the entire map, we return a default message.
This approach not only reduces the cyclomatic complexity but also makes the code more modular and easier to extend. If we need to handle more parameter-value pairs in the future, we can simply add them to the map without modifying the core logic of the function.