How Bhinneka reduce hundreds Lines of Code with JSON Schema Validation
Lines of Code (LOC) is one of software metrics that can represent how big & complex your product is or how inefficient your code is. More lines of code means more effort to maintain it, in reverse the fewer lines of code means the happier you get. One way to solve big product is split it to smaller product or service using Microservices architecture, but still it doesn’t guarantee you get minimum lines of code. Bhinneka itself have several microservices with biggest lines of code service is 104k and smallest lines of code service is 4.6k.
One way to reduce this enormous lines of code is to takeout what process or specs that can live independently, outside your source code, and one of them is validation. Validation is where your code run check from user input, with both positive and negative case must be covered. If user input does not meet your validation rule/ specs then it must return error, otherwise if all validation rule is valid then it can go to the next process, e.g. save to database.
Inline Validation
It is very basic implementation where you do the validation directly on the code and evaluate it one by one on the if statement. Each statement represent one condition of the validation, for example if the request body for specific field is empty.
Sample code for inline validation in Python:
if body['paymentMethodName'] == '': return response(400, message="Payment method can't be blank")
Now, imagine if you have dozens field and each field may have multiple condition for validation, how many line of code it will produce?
Other disadvantage of this approach is that the validation rule is not reusable. If you have other file or module that have similar validation, you have to write it again. Congratulation, you’re adding more line of code to your project and you have break the DRY (Don’t Repeat Yourself) Principle because you have more and more duplication.
Language Validation
This approach is better than inline validation because you don’t have to add many inline if statement for validation. Otherwise, you just define the validation rule on the field definition, you can also put multiple condition on one line, so this will definitely reduce lines of code. You can also create custom validation so you can have reusable rule. Nice!
Sample code for language validation on struct level in Go using Package validator:
type User struct { FirstName string `json:"fname"` LastName string `json:"lname"` Age uint8 `validate:"gte=0,lte=130"` Email string `validate:"required,email"` FavouriteColor string `validate:"hexcolor|rgb|rgba"` }
However, this approach have disadvantage, it’s Language Dependent. Imagine if you want to switch to other programming language, you have to rewrite all the validation rule to new programming language. It is time consuming. Other than that, its still contributing to your lines of code because it’s live inside your source code, not outside. We need an approach that can live independently.
JSON Schema Validation
So what is JSON Schema? From its official website https://json-schema.org/:
JSON Schema is a vocabulary that allows you to annotate and validate JSON documents.
JSON Schema can be used to validate your API client JSON request body, validate API response, or any other use case, it depends on you needs. Comparing from two previous approach, this approach is perfect for our requirements, which is:
- Reduce lines of code, because its live outside your source code, it wouldn’t be counted as your lines of code. Remember less code means less effort to maintain it.
- Reusable, you can create custom rule, and even reference to other schema.
- Language Independent, if you planning to switch to other programming language, you can use the same validation rule without rewriting it.
Sample JSON Schema file, let’s named it user_schema_create_params.json
:
[ { "id": "user_schema_create_params", "$schema": "http://json-schema.org/draft-04/schema#", "description": "schema create new user", "type": "object", "properties": { "name": { "type": "string", "maxLength": 70, "pattern": "^[a-zA-Z0-9\\,. \\/\\()-]+$" }, "email": { "type": "string", "maxLength": 50, "format": "email" }, "department": { "type": "string", "maxLength": 50, "pattern": "^[a-zA-Z0-9\\,. \\/\\()-]+$" }, "phone": { "type": "string", "maxLength": 30, "pattern": "^[0-9\\()-]+$" } }, "required": ["name", "email", "departement", "phone"] } ]
Now you can load the JSON Schema file and use supported library to validate it.
Sample code for JSON Schema validation in Go using gojsonschema:
schemaLoader := gojsonschema.NewReferenceLoader("file:///project/user_schema_create_params.json") documentLoader := gojsonschema.NewReferenceLoader("file:///project/document.json") result, err := gojsonschema.Validate(schemaLoader, documentLoader) if err != nil { panic(err.Error()) } if result.Valid() { fmt.Printf("The document is valid\n") } else { fmt.Printf("The document is not valid. see errors :\n") for _, desc := range result.Errors() { fmt.Printf("- %s\n", desc) } }
Sample code for JSON Schema validation in Python using jsonschema:
import jsonschema import simplejson as json with open('user_schema_create_params.json', 'r') as f: schema_data = f.read() schema = json.loads(schema_data) json_obj = {"name": "John Doe", "email": "mail@example.com", "department": "IT", "phone": "0123456789"} jsonschema.validate(json_obj, schema)