When we talk about building complex software solutions, consequential implementation of all subsystems they comprise is not always a preferable approach. Often, separate parts of the app are developed simultaneously to merge into one at some point. However, the software development process is often associated with many unforeseen changes that the team needs to be ready to adapt to on the fly.
The question is how to increase the team’s efficiency and reduce waste when different subsystems of a software product are implemented in parallel. Contract driven development is an approach that, among other things, can help developers to solve the described issue. Today, we’ll consider its key features and look at a small example explaining how it works in practice.
Contract Driven Development in a Nutshell
This method of building software has several names. It’s also known as design by contract, for example. Here, contracts describe the details of interactions between different components the overall system is composed of. As you can imagine, in this scenario, it’s crucial to describe what a specific application feature must do as accurately as possible. It makes the work of business analysts in the initial stages of the project pretty important since it’s vital to get as much data related to project requirements as possible.
Also, a detailed specification, even if there’s no guarantee that the final solution will correspond to it fully and include all described features, can become a decent basis for further testing and debugging. In case of using this approach, the specification is associated with every subsystem of the software product and serves as an agreement describing the details of communication between the application elements and the outside world.
The very basis of this method is inherited from the business world and brings core features of contract-based relationships to the software industry. Say when client and supplier sign an agreement, both of them have certain obligations and expect some benefits. For example, the client needs ten apples to cook some delicious pies. He’s obliged to meet the cargo at a certain place and at a certain time, and pay for the delivery. As a benefit, the client will cover the need for culinary ingredients.
The supplier, in his turn, must deliver the cargo of apples in appropriate condition. As a benefit, he’ll get rid of these apples, the production of which is his chief object, but in which he has no interest. Here, the primary intention of a contract is to protect both parties and ensure that their interests are met. The same idea can work in software development as well. Imagine that we replace the flow of products with the flow of data, and the application subsystems will take the place of clients and suppliers.
In the case of web development, we can easily find the two subsystems that make up web apps to show how the use of the described approach can help. Front-end and back-end are two concepts that first come into mind when we need to separate a complex software project into smaller parts without delving too deep into development features.
Waiting until the server-side part of a software product is finished to start developing the user interface is not the most optimal approach since, in this case, the project will take too much time. Also, in some cases, the client needs to implement a new feature as quickly as possible or the project needs to be released by a certain date. For example, the business enters a new market which requires implementing some new features. Therefore, when back-end and front-end teams work together on a software project, they agree on some API features and work on the application in parallel. To avoid a situation where one task blocks the others and improves the collaboration between teams, the development approach that includes design by contract method can be used. In this scenario, developers can use the following algorithm, for example:
- Divide a feature to be implemented into two smaller systems, where one is considered a client and the other a supplier;
- Describe in detail their agreements between each other;
- Break subsystems into tasks to be developed separately;
- Send fake data to the customer subsystem to test its work;
- Implement and test both subsystems in parallel;
- Remove the source of fake data for the customer subsystem and merge both parts of a software system into one.
Pros and Cons of Contract Driven Software Development
Pros:
- Independent source of actual and verified data for back-end and front-end teams;
- Good foundation for self-updating documentation that always remains relevant;
- Great starting point for creating CI/CD or other sort of automation processes;
- Non-programmers can read contracts with ease and support them with business requirements by adding simple updates of YAML files.
Cons:
- Extra level of abstraction, which should be supported by the teams;
- Some changes on one side can break compatibility with another side (front-end or back-end part of a software system);
- Some features of this approach may make it too complicated to adopt for small projects with one full stack developer.
Now, let’s consider a small practical example to see how design by contract works in real life.
Example of Implementing Contract Driven Development Approach
Let’s consider a small example of how we can help to improve the collaboration between teams involved in the back-end and front-end parts of the software system. Suppose that developers have the following feature to implement: they should receive data from the back-end to be displayed on the front-end.
With simple grid data, we can work with a plain array of items containing everything we need to display. Here’s an example of such an array:
1 2 3 4 5 6 7 8 9 10 11 12 |
GridData: type: object properties: id: type: number name: type: string age: type: number required: - id - name |
The development team responsible for the client-side of the software product can inform the back-end architect about such important data as the required fields. It’s possible to identify necessary and optional fields and go ahead with the rest of the tasks.
Everything gets more complicated in the step of dynamic data loading. It’s not a simple task to do, especially considering many minor details. Also, it’s not as easy as you may think to build an efficient collaboration mechanism for back-end and front-end developers for task fulfillment.
Front-end part of Webix has extremely good built-ins, and we can use this feature to set up dynamic data loading on the front-end using just a few configuration lines. However, everything’s different when we decide to pass the URL as a data source instead of a plain array of items.
API requests for new data will remain the same, but the response should contain a specific field, showing the availability of data required for handshake for dynamic loading.
We can either try to explain everything to the back-end developers step-by-step in a long call or change specifications in openAPI or any other tool with similar features to describe required changes in API:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
GridDataResponse: type: object properties: data: type: array items: $ref: '#/components/schemas/GridData' total_count: type: number pos: type: number required: - data - total_count - pos |
At this point, we are still linking our dataset to GridData schema reference, but we introduced the way to respond to data to turn on a mechanism of dynamic loading. The question is, how can we mention the required fields in request for a handshake? Here’s an example:
1 2 3 4 5 6 7 8 9 10 |
- name: start in: query description: Starting position for dynamic loading schema: type: number - name: count in: query description: Number of elements to load for dynamic usage schema: type: number |
We have a specific place to insert this data in request description alongside request configurations.
Conclusions
Following these simple steps, we can agree on the shape of data to turn on dynamic loading in a few moves without hours of sync-up calls between development teams! Here, such an approach helps to determine some vital features of the software project before separated teams start implementing them. It helps to avoid misunderstandings between involved parties and automate the workflow.
If you want to make sure that your project is in the right hands, contact us.