How 5xRuby Maintains the Quality of Code through Continuous Integration
Software quality has always been our first priority in handling projects for our customers. Code quality is important because it affects not only the quality of the finished product, but also the level of its maintainability. To provide high-quality project, we have introduced various types of testing, such as unit and integration testing, since 2016. Through this, we hope to provide our customers with projects using the highest quality code.
CHOOSING THE RIGHT WRITING METHOD TO CREATE HIGH-QUALITY PROJECT
Every experienced engineer is well aware that testing is conducted not simply to ensure code quality. More importantly, they may reconsider and refine their code in the writing process while choosing the most suitable method to write code that meets the requirements of customers and projects.
If the code of a project has not undergone rethinking and refinement, problems such as limited extensibility, poor performance, and low maintainability may gradually emerge with the expansion of functions in the future development. Accordingly, it may become impossible for us to efficiently train our personnel to develop new features and maintain products.
How does 5xRuby train engineers to reconsider and refine the code they have written? We adopt static analysis, unit testing, and various other approaches. In order for the finished product to pass the static analysis in terms of recommended style and writing method, we will discuss code that falls short of our standard one by one when conducting code reviews. Based on the review suggestions, we examine the differences in code writing using different programming languages, and ensure the code can express the intent in a semantic manner when naming variables or reading processes.
In addition, we also require our engineers to write unit tests for their own code. The projects we deal with during most learning stages are not too complicated. However, verification becomes increasingly intricate due to various forms of business logic and customized design of our customers. Therefore, we analyze and review each form of business logic to produce suitable interface planning. Further, we conduct unit testing to verify no excessively complex logic is encapsulated in the object and that the code can be called and split in a simple manner.
To illustrate this, let us consider an example from our everyday life.
Suppose we are making a wardrobe now. The production process can be divided into various procedures, including planning and design, wood cutting, hole drilling, wood joining, polishing, and varnishing. All steps can be further divided into sub-steps. For example, the stage of planning and design involves various types of work and design, e.g., material selection, spatial measurement, drawer design, door opening and closing mechanism design, measuring internal wardrobe dimensions, etc. As for wood cutting, it includes wood grain selection, texture selection, and so on. Each step can be subdivided into countless smaller steps.
If a process has an overly complex design, then error tracking will become thorny when an error occurs. For example, during the stage of wood joining, you find the door unlockable when hardware fittings are installed. This may be because the person in charge of material selection chose wrong hardware fittings, or because the designer took a wrong measurement. However, as there has been too much work involved by this stage, it is difficult to find out the root of the problem and therefore there is no other way than to dismantle the half-finished wardrobe and start all over again.
Encapsulating overly complicated logic in an object is exactly like this. When an error occurs, error correction and debugging will take enormous amounts of time and effort. To avoid this, we narrow the scope of our inspections as much as possible. This not only facilitates error tracking at every working stage but also enables engineers to correct errors in a more efficient manner.
The above-mentioned procedures for making a wardrobe (e.g. wood selection, screw positioning, hole drilling, etc.) can be seen as “units”. The unit testing mentioned earlier is performed to check if all units (actions) are accurately executed. If every unit (action) passes the test, the overall operation of all units combined will naturally produce a lower error rate.
IMPLEMENTING UNIT TESTING TO FULLY MEET CUSTOMER EXPECTATIONS
As shown above, unit testing plays a critical role in building a stable system.
However, writing tests is already a huge challenge for most engineers. Therefore, it has always been our objective to train engineers from writing tests to understanding and making good use of them. In test writing, we require their tests to be concise and neat. Take the suggestions of RuboCop as an example. Typically, a test should not contain too many reference variables. This directly reflects whether we have set too many conditions in a test or whether the original object has such potential problems as excessive complexity or parameters.
This is because when we design overly complex logic, it is usually very difficult to test (see the wardrobe example in the previous paragraph). Therefore, we encourage engineers to discuss and analyze whether the source code can implement SOLID principles while achieving the goal of “unit” testing.
When writing tests, we continuously contemplate on how to isolate maintenance challenges caused by complex design and third-party dependencies from unit testing and successfully include them into integration testing and other types of testing.
In addition to painstakingly enhancing the quality of the source code, we effectively use testing to facilitate code modification and refactoring. Aside from our own code, we utilize an assortment of testing techniques to gradually improve projects that customers have difficulty maintaining by themselves, while making long-term maintenance and operation possible.
STANDARDIZING CODING STYLE TO IMPROVE WRITING QUALITY
We regularly use RuboCop as a tool for static code analysis during project development. We will first create a standard style guide based on user habits to quickly familiarize new partners with the coding style we use at 5xRuby. This guide also helps explain our reasoning and logic in code writing to new engineers regarding the issues detected by analysis tools.
Through this, most of our engineers can change their coding habits within two weeks and deliver quality source code to our customers, which helps reduce additional costs incurred when a project changes hands during staff adjustment.
CONDUCTING STRICT CYBERSECURITY REVIEWS TO DELIVER HIGH-SECURITY PROJECTS
The cybersecurity field requires a high level of professionalism. With abundant experience in the use of Ruby on Rails and the help of development frameworks, 5xRuby effectively resolves various low-level security issues. To keep an acute awareness of cybersecurity, we have applied two sets of tools, Breakman and Bundler Audit, to every project. The former functions as a static analysis tool like RuboCop. Specifically, it issues warnings regarding problematic code overlooked in Ruby on Rails, so that engineers may stay alert to cybersecurity issues at all times.
As for Bundler Audit, it provides a complete database for security vulnerabilities in Ruby packages. During our development, we will continuously extract information about the latest security vulnerabilities and promptly update packages where those vulnerabilities are detected, while providing our customers with the most secure and stable projects.
CONDUCTING CONTINUOUS INTEGRATION TO BOOST DEVELOPMENT EFFICIENCY
If the above analysis and inspection are performed by engineers themselves, problems are likely to be overlooked. Therefore, we use the Continuous Integration (CI) tools provided by our internal GitLab to build a large number of checkpoints, so that various inspections using different tools may be conducted when an engineer sends a merge request or when other projects are in operation. By accelerating development and increasing code review efficiency, we offer our customers fast and high-quality development.
It can be observed from the above figure that we implemente corresponding inspections for both back-end and front-end development, while examining the outputs of our engineers against rigorous standards. Through continuous integration, we are able to maintain the soundness and stability of the source code in our projects and efficiently train our engineers to consistently produce high-quality source code.
MONITORING QUALITY AND CONTINUOUSLY IMPROVING PROGRAMS
For a more comprehensive understanding of the quality and development of each project, we use SonarQube as our quality analysis console. By integrating the reports collected from continuous integration into SonarQube, we can check whether each project has been persistently and positively developed and whether the test coverage, bad smell , technical debt, and other issues have been continuously improved.
With the help of these tools, we have continued to enhance the quality of code 5xRuby provides during development, seeking to continuously advance with the times.
 Bad smell generally refers to code that may have an adverse effect on programs. It may be excessively long or incomprehensible code, or code that is hard to split due to excessive reliance on certain functions.
Code quality has always been a fundamental priority for us. By continuously improving the quality of code, we hope to speed up our development to fulfill the business requirements of our customers and to stay relevant with the market. In addition, we wish to deliver source codes that would take our customers substantially less time to process while eliminating potential future risks.
As the technical director of 5xRuby, I believe it is the mission and never-ending pursuit of our entire team to deliver the highest quality source code to our customers.